From ed76a4ee96d3a0ea40b6bf4d1447f2af49cb9f7f Mon Sep 17 00:00:00 2001 From: Mickael Istria Date: Tue, 2 Apr 2019 00:43:35 +0200 Subject: Bug 546020 - [code mining] "Index out of Bounds" when inline w/ folding Change-Id: I7b10e2d8122372bec5452225313142cf6ec58f3c Signed-off-by: Mickael Istria --- org.eclipse.jface.text.tests/META-INF/MANIFEST.MF | 3 +- org.eclipse.jface.text.tests/pom.xml | 2 +- .../eclipse/jface/text/tests/CodeMiningTest.java | 181 -------------------- .../text/tests/DelayedEchoCodeMiningProvider.java | 64 ------- .../jface/text/tests/JFaceTextTestSuite.java | 5 +- .../jface/text/tests/ScreenshotOnFailureRule.java | 2 +- .../codemining/CodeMiningProjectionViewerTest.java | 162 ++++++++++++++++++ .../text/tests/codemining/CodeMiningTest.java | 183 +++++++++++++++++++++ .../codemining/DelayedEchoCodeMiningProvider.java | 64 +++++++ .../codemining/StaticContentLineCodeMining.java | 32 ++++ .../source/inlined/AbstractInlinedAnnotation.java | 16 +- .../inlined/InlinedAnnotationDrawingStrategy.java | 22 +-- .../text/source/inlined/LineContentAnnotation.java | 10 +- 13 files changed, 479 insertions(+), 267 deletions(-) delete mode 100644 org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/CodeMiningTest.java delete mode 100644 org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/DelayedEchoCodeMiningProvider.java create mode 100644 org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningProjectionViewerTest.java create mode 100644 org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java create mode 100644 org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/DelayedEchoCodeMiningProvider.java create mode 100644 org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java diff --git a/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF b/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF index 7194ac38c..4d334bb30 100644 --- a/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF @@ -2,11 +2,12 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.jface.text.tests -Bundle-Version: 3.11.600.qualifier +Bundle-Version: 3.11.700.qualifier Bundle-Vendor: %Plugin.providerName Bundle-Localization: plugin Export-Package: org.eclipse.jface.text.tests, + org.eclipse.jface.text.tests.codemining, org.eclipse.jface.text.tests.contentassist, org.eclipse.jface.text.tests.reconciler, org.eclipse.jface.text.tests.rules, diff --git a/org.eclipse.jface.text.tests/pom.xml b/org.eclipse.jface.text.tests/pom.xml index 25643b9d0..18ea858c5 100644 --- a/org.eclipse.jface.text.tests/pom.xml +++ b/org.eclipse.jface.text.tests/pom.xml @@ -19,7 +19,7 @@ org.eclipse.jface org.eclipse.jface.text.tests - 3.11.600-SNAPSHOT + 3.11.700-SNAPSHOT eclipse-test-plugin ${project.artifactId} diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/CodeMiningTest.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/CodeMiningTest.java deleted file mode 100644 index a02f64b5b..000000000 --- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/CodeMiningTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Red Hat Inc. and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * - Mickael Istria (Red Hat Inc.) - initial implementation - *******************************************************************************/ -package org.eclipse.jface.text.tests; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.StyledText; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -import org.eclipse.jface.util.Util; - -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.Document; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.codemining.ICodeMiningProvider; -import org.eclipse.jface.text.reconciler.DirtyRegion; -import org.eclipse.jface.text.reconciler.IReconcilingStrategy; -import org.eclipse.jface.text.reconciler.MonoReconciler; -import org.eclipse.jface.text.source.AnnotationModel; -import org.eclipse.jface.text.source.AnnotationPainter; -import org.eclipse.jface.text.source.SourceViewer; -import org.eclipse.jface.text.tests.util.DisplayHelper; - -public class CodeMiningTest { - - private SourceViewer fViewer; - private Shell fShell; - - @Rule public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(); - - @Before - public void setUp() { - fShell= new Shell(Display.getDefault()); - fShell.setSize(500, 200); - fShell.setLayout(new FillLayout()); - fViewer= new SourceViewer(fShell, null, SWT.NONE); - final StyledText textWidget= fViewer.getTextWidget(); - MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { - @Override - public void setDocument(IDocument document) { - fViewer.updateCodeMinings(); - } - - @Override - public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { - // nothing to do - } - - @Override - public void reconcile(IRegion partition) { - fViewer.updateCodeMinings(); - } - }, false); - reconciler.install(fViewer); - fViewer.setDocument(new Document(), new AnnotationModel()); - fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new DelayedEchoCodeMiningProvider() }); - AnnotationPainter annotationPainter = new AnnotationPainter(fViewer, null); - fViewer.setCodeMiningAnnotationPainter(annotationPainter); - fViewer.addPainter(annotationPainter); - // this currently needs to be repeated - fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new DelayedEchoCodeMiningProvider() }); - final Display display = textWidget.getDisplay(); - fShell.open(); - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return fViewer.getTextWidget().isVisible(); - } - }.waitForCondition(display, 3000)); - DisplayHelper.sleep(textWidget.getDisplay(), 1000); - } - - @After - public void tearDown() { - fShell.dispose(); - fViewer = null; - } - - @Test - public void testCodeMiningFirstLine() { - fViewer.getDocument().set("echo"); - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return fViewer.getTextWidget().getLineVerticalIndent(0) > 0; - } - }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); - } - - @Test - public void testCodeMiningCtrlHome() throws BadLocationException { - Assume.assumeFalse("See bug 541415. For whatever reason, this shortcut doesn't work on Mac", Util.isMac()); - DelayedEchoCodeMiningProvider.DELAY = 500; - fViewer.getDocument().set(TextViewerTest.generate5000Lines()); - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return fViewer.getTextWidget().getText().length() > 5000; - } - }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); - TextViewerTest.ctrlEnd(fViewer); - final int lastLine = fViewer.getDocument().getNumberOfLines() - 1; - final int lastLineOffset = fViewer.getDocument().getLineOffset(lastLine); - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return lastLineOffset >= fViewer.getVisibleRegion().getOffset() && lastLineOffset <= fViewer.getVisibleRegion().getOffset() + fViewer.getVisibleRegion().getLength(); - } - }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); - DisplayHelper.sleep(fViewer.getControl().getDisplay(), 500); - AtomicInteger events = new AtomicInteger(); - fViewer.addViewportListener(offset -> - events.incrementAndGet()); - TextViewerTest.ctrlHome(fViewer); - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return events.get() > 0; - } - }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); - Assert.assertEquals(0, fViewer.getVisibleRegion().getOffset()); - // wait for codemining to style line - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return fViewer.getTextWidget().getLineVerticalIndent(0) > 0; - } - }.waitForCondition(fViewer.getControl().getDisplay(), 300000)); - } - - @Test - public void testCodeMiningCtrlEnd() throws BadLocationException { - Assume.assumeFalse("See bug 541415. For whatever reason, this shortcut doesn't work on Mac", Util.isMac()); - fViewer.getDocument().set(TextViewerTest.generate5000Lines()); - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return fViewer.getTextWidget().getText().length() > 5000 && fViewer.getTextWidget().getLineVerticalIndent(0) > 0; - } - }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); - DisplayHelper.sleep(fViewer.getTextWidget().getDisplay(), 500); - TextViewerTest.ctrlEnd(fViewer); - final int lastLine = fViewer.getDocument().getNumberOfLines() - 1; - final int lastLineOffset = fViewer.getDocument().getLineOffset(lastLine); - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return lastLineOffset >= fViewer.getVisibleRegion().getOffset() && lastLineOffset <= fViewer.getVisibleRegion().getOffset() + fViewer.getVisibleRegion().getLength(); - } - }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); - Assert.assertTrue(new DisplayHelper() { - @Override - protected boolean condition() { - return fViewer.getTextWidget().getLineVerticalIndent(lastLine) > 0; - } - }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); - } - -} diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/DelayedEchoCodeMiningProvider.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/DelayedEchoCodeMiningProvider.java deleted file mode 100644 index 8dd93c96f..000000000 --- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/DelayedEchoCodeMiningProvider.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Red Hat Inc. and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * - Mickael Istria (Red Hat Inc.) - initial implementation - *******************************************************************************/ -package org.eclipse.jface.text.tests; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import org.eclipse.core.runtime.IProgressMonitor; - -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.ITextViewer; -import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; -import org.eclipse.jface.text.codemining.ICodeMining; -import org.eclipse.jface.text.codemining.ICodeMiningProvider; -import org.eclipse.jface.text.codemining.LineHeaderCodeMining; - -public class DelayedEchoCodeMiningProvider extends AbstractCodeMiningProvider implements ICodeMiningProvider { - - public static int DELAY = 0; - - @Override - public CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { - return CompletableFuture.supplyAsync(() -> { - try { - Thread.sleep(DELAY); - } catch (InterruptedException e1) { - e1.printStackTrace(); - return null; - } - IDocument document = viewer.getDocument(); - List res = new ArrayList<>(); - for (int lineNumber = 0; lineNumber < document.getNumberOfLines(); lineNumber++) { - try { - String lineContent = document.get(document.getLineOffset(lineNumber), document.getLineLength(lineNumber)); - if (!lineContent.trim().isEmpty()) { - LineHeaderCodeMining mining = new LineHeaderCodeMining(lineNumber, document, DelayedEchoCodeMiningProvider.this) { - // Nothing in particular - }; - mining.setLabel(lineContent); - res.add(mining); - } - } catch (BadLocationException e) { - e.printStackTrace(); - } - - } - return res; - }); - } - -} diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java index 7e822ffd8..d35ff7e74 100644 --- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java @@ -17,6 +17,8 @@ import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; +import org.eclipse.jface.text.tests.codemining.CodeMiningTest; +import org.eclipse.jface.text.tests.codemining.CodeMiningProjectionViewerTest; import org.eclipse.jface.text.tests.contentassist.AsyncContentAssistTest; import org.eclipse.jface.text.tests.reconciler.AbstractReconcilerTest; import org.eclipse.jface.text.tests.rules.DefaultPartitionerTest; @@ -55,7 +57,8 @@ import org.eclipse.jface.text.tests.templates.persistence.TemplatePersistenceDat WordRuleTest.class, TemplatePersistenceDataTest.class, - CodeMiningTest.class + CodeMiningTest.class, + CodeMiningProjectionViewerTest.class }) public class JFaceTextTestSuite { // see @SuiteClasses diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java index 3deb57992..da2da9594 100644 --- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java @@ -17,7 +17,7 @@ import org.junit.rules.TestWatcher; import org.eclipse.test.Screenshots; -final class ScreenshotOnFailureRule extends TestWatcher { +public final class ScreenshotOnFailureRule extends TestWatcher { @Override protected void failed(Throwable e, org.junit.runner.Description description) { Screenshots.takeScreenshot(description.getTestClass(), description.getMethodName()); diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningProjectionViewerTest.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningProjectionViewerTest.java new file mode 100644 index 000000000..9e06ba19b --- /dev/null +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningProjectionViewerTest.java @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2019 Red Hat Inc., and others + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * - Mickael Istria (Red Hat Inc.) + */ +package org.eclipse.jface.text.tests.codemining; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.osgi.framework.Bundle; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.ILogListener; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.codemining.ICodeMining; +import org.eclipse.jface.text.codemining.ICodeMiningProvider; +import org.eclipse.jface.text.codemining.LineContentCodeMining; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationPainter; +import org.eclipse.jface.text.source.IAnnotationAccess; +import org.eclipse.jface.text.source.ISharedTextColors; +import org.eclipse.jface.text.source.projection.ProjectionAnnotation; +import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; +import org.eclipse.jface.text.source.projection.ProjectionSupport; +import org.eclipse.jface.text.source.projection.ProjectionViewer; +import org.eclipse.jface.text.tests.util.DisplayHelper; + +public class CodeMiningProjectionViewerTest { + + private final class RepeatLettersCodeMiningProvider implements ICodeMiningProvider { + @Override + public CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { + List codeMinings = new ArrayList<>(); + for (int i = 0; i < viewer.getDocument().getLength(); i++) { + try { + char c= viewer.getDocument().getChar(i); + if (Character.isLetter(c)) { + codeMinings.add(new StaticContentLineCodeMining(i, c, this)); + } + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + return CompletableFuture.completedFuture(codeMinings); + } + + @Override + public void dispose() { + } + } + + private Shell fParent; + private ProjectionViewer fViewer; + + @Before + public void setUp() { + fParent= new Shell(); + fParent.setSize(500, 200); + fParent.setLayout(new FillLayout()); + fViewer= new ProjectionViewer(fParent, null, null, false, SWT.NONE); + IAnnotationAccess annotationAccess = new IAnnotationAccess() { + @Override + public Object getType(Annotation annotation) { + return annotation.getType(); + } + + @Override + public boolean isMultiLine(Annotation annotation) { + return true; + } + + @Override + public boolean isTemporary(Annotation annotation) { + return true; + } + }; + // code minings + AnnotationPainter painter = new AnnotationPainter(fViewer, annotationAccess); + fViewer.addPainter(painter); + fViewer.setCodeMiningAnnotationPainter(painter); + // projection/folding + fViewer.setDocument(new Document(), new ProjectionAnnotationModel()); + ProjectionSupport projectionSupport = new ProjectionSupport(fViewer, annotationAccess, new ISharedTextColors() { + @Override + public Color getColor(RGB rgb) { + return null; + } + + @Override + public void dispose() { + } + }); + projectionSupport.install(); + fViewer.doOperation(ProjectionViewer.TOGGLE); + } + + @After + public void tearDown() { + fParent.dispose(); + } + + @Test + public void testCollapse() throws Exception { + fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { + new RepeatLettersCodeMiningProvider() + }); + fViewer.getDocument().set("1a\n2a\n3a\n4a\n5a\n6a\n"); + ProjectionAnnotation annotation= new ProjectionAnnotation(true); + fViewer.getProjectionAnnotationModel().addAnnotation(annotation, new Position(0, fViewer.getDocument().getLineOffset(4))); + fViewer.doOperation(ProjectionViewer.COLLAPSE_ALL); + fViewer.updateCodeMinings(); + fParent.open(); + + Bundle bundle = Platform.getBundle("org.eclipse.ui.workbench"); + ILog log = null; + AtomicReference logError = new AtomicReference<>(); + ILogListener logListener= (status, plugin) -> { + logError.set(status); + }; + if (bundle != null && bundle.getState() == Bundle.ACTIVE) { + log = Platform.getLog(bundle); + log.addLogListener(logListener); + } + try { + // without workbench, next line throws Exception directly + DisplayHelper.sleep(fParent.getDisplay(), 1000); + Assert.assertNull(logError.get()); + } finally { + if (log != null) { + log.removeLogListener(logListener); + } + } + } +} diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java new file mode 100644 index 000000000..cac5fde14 --- /dev/null +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2019 Red Hat Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * - Mickael Istria (Red Hat Inc.) - initial implementation + *******************************************************************************/ +package org.eclipse.jface.text.tests.codemining; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.util.Util; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.codemining.ICodeMiningProvider; +import org.eclipse.jface.text.reconciler.DirtyRegion; +import org.eclipse.jface.text.reconciler.IReconcilingStrategy; +import org.eclipse.jface.text.reconciler.MonoReconciler; +import org.eclipse.jface.text.source.AnnotationModel; +import org.eclipse.jface.text.source.AnnotationPainter; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.text.tests.ScreenshotOnFailureRule; +import org.eclipse.jface.text.tests.TextViewerTest; +import org.eclipse.jface.text.tests.util.DisplayHelper; + +public class CodeMiningTest { + + private SourceViewer fViewer; + private Shell fShell; + + @Rule public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(); + + @Before + public void setUp() { + fShell= new Shell(Display.getDefault()); + fShell.setSize(500, 200); + fShell.setLayout(new FillLayout()); + fViewer= new SourceViewer(fShell, null, SWT.NONE); + final StyledText textWidget= fViewer.getTextWidget(); + MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { + @Override + public void setDocument(IDocument document) { + fViewer.updateCodeMinings(); + } + + @Override + public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { + // nothing to do + } + + @Override + public void reconcile(IRegion partition) { + fViewer.updateCodeMinings(); + } + }, false); + reconciler.install(fViewer); + fViewer.setDocument(new Document(), new AnnotationModel()); + fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new DelayedEchoCodeMiningProvider() }); + AnnotationPainter annotationPainter = new AnnotationPainter(fViewer, null); + fViewer.setCodeMiningAnnotationPainter(annotationPainter); + fViewer.addPainter(annotationPainter); + // this currently needs to be repeated + fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new DelayedEchoCodeMiningProvider() }); + final Display display = textWidget.getDisplay(); + fShell.open(); + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return fViewer.getTextWidget().isVisible(); + } + }.waitForCondition(display, 3000)); + DisplayHelper.sleep(textWidget.getDisplay(), 1000); + } + + @After + public void tearDown() { + fShell.dispose(); + fViewer = null; + } + + @Test + public void testCodeMiningFirstLine() { + fViewer.getDocument().set("echo"); + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return fViewer.getTextWidget().getLineVerticalIndent(0) > 0; + } + }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); + } + + @Test + public void testCodeMiningCtrlHome() throws BadLocationException { + Assume.assumeFalse("See bug 541415. For whatever reason, this shortcut doesn't work on Mac", Util.isMac()); + DelayedEchoCodeMiningProvider.DELAY = 500; + fViewer.getDocument().set(TextViewerTest.generate5000Lines()); + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return fViewer.getTextWidget().getText().length() > 5000; + } + }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); + TextViewerTest.ctrlEnd(fViewer); + final int lastLine = fViewer.getDocument().getNumberOfLines() - 1; + final int lastLineOffset = fViewer.getDocument().getLineOffset(lastLine); + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return lastLineOffset >= fViewer.getVisibleRegion().getOffset() && lastLineOffset <= fViewer.getVisibleRegion().getOffset() + fViewer.getVisibleRegion().getLength(); + } + }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); + DisplayHelper.sleep(fViewer.getControl().getDisplay(), 500); + AtomicInteger events = new AtomicInteger(); + fViewer.addViewportListener(offset -> + events.incrementAndGet()); + TextViewerTest.ctrlHome(fViewer); + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return events.get() > 0; + } + }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); + Assert.assertEquals(0, fViewer.getVisibleRegion().getOffset()); + // wait for codemining to style line + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return fViewer.getTextWidget().getLineVerticalIndent(0) > 0; + } + }.waitForCondition(fViewer.getControl().getDisplay(), 300000)); + } + + @Test + public void testCodeMiningCtrlEnd() throws BadLocationException { + Assume.assumeFalse("See bug 541415. For whatever reason, this shortcut doesn't work on Mac", Util.isMac()); + fViewer.getDocument().set(TextViewerTest.generate5000Lines()); + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return fViewer.getTextWidget().getText().length() > 5000 && fViewer.getTextWidget().getLineVerticalIndent(0) > 0; + } + }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); + DisplayHelper.sleep(fViewer.getTextWidget().getDisplay(), 500); + TextViewerTest.ctrlEnd(fViewer); + final int lastLine = fViewer.getDocument().getNumberOfLines() - 1; + final int lastLineOffset = fViewer.getDocument().getLineOffset(lastLine); + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return lastLineOffset >= fViewer.getVisibleRegion().getOffset() && lastLineOffset <= fViewer.getVisibleRegion().getOffset() + fViewer.getVisibleRegion().getLength(); + } + }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); + Assert.assertTrue(new DisplayHelper() { + @Override + protected boolean condition() { + return fViewer.getTextWidget().getLineVerticalIndent(lastLine) > 0; + } + }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); + } + +} diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/DelayedEchoCodeMiningProvider.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/DelayedEchoCodeMiningProvider.java new file mode 100644 index 000000000..ac976cda6 --- /dev/null +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/DelayedEchoCodeMiningProvider.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2019 Red Hat Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * - Mickael Istria (Red Hat Inc.) - initial implementation + *******************************************************************************/ +package org.eclipse.jface.text.tests.codemining; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; +import org.eclipse.jface.text.codemining.ICodeMining; +import org.eclipse.jface.text.codemining.ICodeMiningProvider; +import org.eclipse.jface.text.codemining.LineHeaderCodeMining; + +public class DelayedEchoCodeMiningProvider extends AbstractCodeMiningProvider implements ICodeMiningProvider { + + public static int DELAY = 0; + + @Override + public CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { + return CompletableFuture.supplyAsync(() -> { + try { + Thread.sleep(DELAY); + } catch (InterruptedException e1) { + e1.printStackTrace(); + return null; + } + IDocument document = viewer.getDocument(); + List res = new ArrayList<>(); + for (int lineNumber = 0; lineNumber < document.getNumberOfLines(); lineNumber++) { + try { + String lineContent = document.get(document.getLineOffset(lineNumber), document.getLineLength(lineNumber)); + if (!lineContent.trim().isEmpty()) { + LineHeaderCodeMining mining = new LineHeaderCodeMining(lineNumber, document, DelayedEchoCodeMiningProvider.this) { + // Nothing in particular + }; + mining.setLabel(lineContent); + res.add(mining); + } + } catch (BadLocationException e) { + e.printStackTrace(); + } + + } + return res; + }); + } + +} diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java new file mode 100644 index 000000000..59ef8f6e6 --- /dev/null +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2019 Red Hat Inc., and others + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * - Mickael Istria (Red Hat Inc.) + */ +package org.eclipse.jface.text.tests.codemining; + +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.codemining.ICodeMiningProvider; +import org.eclipse.jface.text.codemining.LineContentCodeMining; + +public class StaticContentLineCodeMining extends LineContentCodeMining { + + public StaticContentLineCodeMining(int i, char c, ICodeMiningProvider repeatLettersCodeMiningProvider) { + super(new Position(i, 1), repeatLettersCodeMiningProvider); + setLabel(Character.toString(c)); + } + + @Override + public boolean isResolved() { + return true; + } + +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java index 2eeea983c..464186604 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java @@ -22,8 +22,10 @@ import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; +import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.ISourceViewer; @@ -72,14 +74,24 @@ public abstract class AbstractInlinedAnnotation extends Annotation { } /** - * Returns the position where the annotation must be drawn. + * Returns the position where the annotation must be drawn. For {@link ITextViewerExtension5} + * (enabling folding with widget/model projection), this position is the model + * position. * - * @return the position where the annotation must be drawn. + * @return the model position where the annotation must be drawn. */ public Position getPosition() { return position; } + final Position computeWidgetPosition() { + if (fViewer instanceof ITextViewerExtension5) { + IRegion region= ((ITextViewerExtension5) fViewer).modelRange2WidgetRange(new Region(position.getOffset(), position.getLength())); + return new Position(region.getOffset(), region.getLength()); + } + return position; + } + /** * Returns the {@link StyledText} widget where the annotation must be drawn. * diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java index 1bf56916b..c29c6332f 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java @@ -32,7 +32,7 @@ import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy; class InlinedAnnotationDrawingStrategy implements IDrawingStrategy { @Override - public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) { + public void draw(Annotation annotation, GC gc, StyledText textWidget, int widgetoffset, int length, Color color) { if (!(annotation instanceof AbstractInlinedAnnotation)) { return; } @@ -40,7 +40,7 @@ class InlinedAnnotationDrawingStrategy implements IDrawingStrategy { // The annotation is not in visible lines, don't draw it. return; } - draw((AbstractInlinedAnnotation) annotation, gc, textWidget, offset, length, + draw((AbstractInlinedAnnotation) annotation, gc, textWidget, widgetoffset, length, color); } @@ -50,16 +50,16 @@ class InlinedAnnotationDrawingStrategy implements IDrawingStrategy { * @param annotation the annotation to be drawn * @param gc the graphics context, null when in clearing mode * @param textWidget the text widget to draw on - * @param offset the offset of the line + * @param widgetOffset the offset of the line * @param length the length of the line * @param color the color of the line */ - public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText textWidget, int offset, int length, + public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) { if (annotation instanceof LineHeaderAnnotation) { - draw((LineHeaderAnnotation) annotation, gc, textWidget, offset, length, color); + draw((LineHeaderAnnotation) annotation, gc, textWidget, widgetOffset, length, color); } else { - draw((LineContentAnnotation) annotation, gc, textWidget, offset, length, color); + draw((LineContentAnnotation) annotation, gc, textWidget, widgetOffset, length, color); } } @@ -125,16 +125,16 @@ class InlinedAnnotationDrawingStrategy implements IDrawingStrategy { * @param annotation the annotation to be drawn * @param gc the graphics context, null when in clearing mode * @param textWidget the text widget to draw on - * @param offset the offset of the line + * @param widgetOffset the offset of the line in the widget (not model) * @param length the length of the line * @param color the color of the line */ - private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int offset, int length, + private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) { - if (annotation.drawRightToPreviousChar(offset)) { - drawAsRightOfPreviousCharacter(annotation, gc, textWidget, offset, length, color); + if (annotation.drawRightToPreviousChar(widgetOffset)) { + drawAsRightOfPreviousCharacter(annotation, gc, textWidget, widgetOffset, length, color); } else { - drawAsLeftOf1stCharacter(annotation, gc, textWidget, offset, length, color); + drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color); } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java index 76c97148b..332a8a583 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java @@ -108,15 +108,15 @@ public class LineContentAnnotation extends AbstractInlinedAnnotation { * @return the style to apply with GlyphMetrics width only if needed. */ StyleRange updateStyle(StyleRange style) { - boolean usePreviousChar= drawRightToPreviousChar(getPosition().getOffset()); + Position widgetPosition= computeWidgetPosition(); + boolean usePreviousChar= drawRightToPreviousChar(widgetPosition.getOffset()); if (width == 0 || getRedrawnCharacterWidth() == 0) { return null; } int fullWidth= width + getRedrawnCharacterWidth(); if (style == null) { style= new StyleRange(); - Position position= getPosition(); - style.start= position.getOffset(); + style.start= widgetPosition.getOffset(); if (usePreviousChar) { style.start--; } @@ -144,8 +144,8 @@ public class LineContentAnnotation extends AbstractInlinedAnnotation { return style; } - boolean drawRightToPreviousChar(int offset) { - return getTextWidget().getLineAtOffset(offset) == getTextWidget().getLineAtOffset(offset - 1); + boolean drawRightToPreviousChar(int widgetOffset) { + return getTextWidget().getLineAtOffset(widgetOffset) == getTextWidget().getLineAtOffset(widgetOffset - 1); } } -- cgit v1.2.1