diff options
author | Victor Rubezhny | 2021-10-29 21:58:02 +0000 |
---|---|---|
committer | Mickael Istria | 2021-11-29 10:28:50 +0000 |
commit | 34cc626c0956f31f4ba2821e957dc2645e83df8b (patch) | |
tree | fcdc456cc0796470e1778d62b718149e964b2c43 | |
parent | e0da71e663d6a6df458f2a60d11ef9a31e7027cd (diff) | |
download | eclipse.platform.text-34cc626c0956f31f4ba2821e957dc2645e83df8b.tar.gz eclipse.platform.text-34cc626c0956f31f4ba2821e957dc2645e83df8b.tar.xz eclipse.platform.text-34cc626c0956f31f4ba2821e957dc2645e83df8b.zip |
Bug 546106 - "Show Tooltip description"/F2 seems non functional
Change-Id: I973cd0814440ce0d657f1a0ff80774e5a5c76785
Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.text/+/187184
Tested-by: Platform Bot <platform-bot@eclipse.org>
Reviewed-by: Mickael Istria <mistria@redhat.com>
5 files changed, 295 insertions, 3 deletions
diff --git a/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF b/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF index 36d6a46df63..ecdf8d56732 100644 --- a/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.ui.genericeditor.tests;singleton:=true -Bundle-Version: 1.2.0.qualifier +Bundle-Version: 1.2.100.qualifier Bundle-Vendor: %Plugin.providerName Bundle-Localization: plugin Export-Package: org.eclipse.ui.genericeditor.tests, diff --git a/org.eclipse.ui.genericeditor.tests/pom.xml b/org.eclipse.ui.genericeditor.tests/pom.xml index fcff61027bb..735c35c4ccd 100644 --- a/org.eclipse.ui.genericeditor.tests/pom.xml +++ b/org.eclipse.ui.genericeditor.tests/pom.xml @@ -19,7 +19,7 @@ </parent> <groupId>org.eclipse.ui</groupId> <artifactId>org.eclipse.ui.genericeditor.tests</artifactId> - <version>1.2.0-SNAPSHOT</version> + <version>1.2.100-SNAPSHOT</version> <packaging>eclipse-test-plugin</packaging> <properties> <testSuite>${project.artifactId}</testSuite> diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestSuite.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestSuite.java index b1d9b97cb81..12958417f56 100644 --- a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestSuite.java +++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestSuite.java @@ -25,6 +25,7 @@ import org.junit.runners.Suite.SuiteClasses; ContextInfoTest.class, StylingTest.class, HoverTest.class, + ShowInformationTest.class, EditorTest.class, FoldingTest.class, AutoEditTest.class, diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/ShowInformationTest.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/ShowInformationTest.java new file mode 100644 index 00000000000..4d5da0783d1 --- /dev/null +++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/ShowInformationTest.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2021 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: + * Red Hat Inc. + *******************************************************************************/ +package org.eclipse.ui.genericeditor.tests; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.core.runtime.Platform; + +import org.eclipse.text.tests.Accessor; + +import org.eclipse.jface.text.AbstractInformationControl; +import org.eclipse.jface.text.AbstractInformationControlManager; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.text.tests.util.DisplayHelper; + +import org.eclipse.ui.genericeditor.tests.contributions.AlrightyHoverProvider; + +import org.eclipse.ui.workbench.texteditor.tests.ScreenshotTest; + +import org.eclipse.ui.texteditor.AbstractTextEditor; + +/** + * @since 1.2 + */ +public class ShowInformationTest extends AbstratGenericEditorTest { + + @Rule + public TestName testName= new TestName(); + + @Before + public void skipOnNonLinux() { + Assume.assumeFalse("This test currently always fail on Windows (bug 505842), skipping", Platform.OS_WIN32.equals(Platform.getOS())); + Assume.assumeFalse("This test currently always fail on macOS (bug 505842), skipping", Platform.OS_MACOSX.equals(Platform.getOS())); + } + + @Test + public void testInformationControl() throws Exception { + Shell shell= getHoverShell(triggerCompletionAndRetrieveInformationControlManager(), true); + assertNotNull(findControl(shell, StyledText.class, AlrightyHoverProvider.LABEL)); + } + + private Shell getHoverShell(AbstractInformationControlManager manager, boolean failOnError) { + AbstractInformationControl[] control= { null }; + new DisplayHelper() { + @Override + protected boolean condition() { + control[0]= (AbstractInformationControl) new Accessor(manager, AbstractInformationControlManager.class).get("fInformationControl"); + return control[0] != null; + } + }.waitForCondition(this.editor.getSite().getShell().getDisplay(), 5000); + if (control[0] == null) { + if (failOnError) { + ScreenshotTest.takeScreenshot(getClass(), testName.getMethodName(), System.out); + fail(); + } else { + return null; + } + } + boolean[] result = {false}; + Shell shell= (Shell) new Accessor(control[0], AbstractInformationControl.class).get("fShell"); + new DisplayHelper() { + @Override + protected boolean condition() { + return (result[0] = shell.isVisible()); + } + }.waitForCondition(control[0].getShell().getDisplay(), 2000); + if (failOnError) { + assertTrue(shell.isVisible()); + } + return shell; + } + + private <T extends Control> T findControl(Control control, Class<T> controlType, String label) { + if (control.getClass() == controlType) { + @SuppressWarnings("unchecked") + T res= (T) control; + if (label == null) { + return res; + } + String controlLabel= null; + if (control instanceof Label) { + controlLabel= ((Label) control).getText(); + } else if (control instanceof Link) { + controlLabel= ((Link) control).getText(); + } else if (control instanceof Text) { + controlLabel= ((Text) control).getText(); + } else if (control instanceof StyledText) { + controlLabel= ((StyledText) control).getText(); + } + if (controlLabel != null && controlLabel.contains(label)) { + return res; + } + } else if (control instanceof Composite) { + for (Control child : ((Composite) control).getChildren()) { + T res= findControl(child, controlType, label); + if (res != null) { + return res; + } + } + } + return null; + } + + private Object getShowInformationData(AbstractInformationControlManager manager) { + return new Accessor(manager, AbstractInformationControlManager.class).get("fInformation"); + } + + private AbstractInformationControlManager triggerCompletionAndRetrieveInformationControlManager() { + final int caretLocation= 2; + this.editor.selectAndReveal(caretLocation, 0); + final StyledText editorTextWidget= (StyledText) this.editor.getAdapter(Control.class); + new DisplayHelper() { + @Override + protected boolean condition() { + return editorTextWidget.isFocusControl() && editorTextWidget.getSelection().x == caretLocation; + } + }.waitForCondition(editorTextWidget.getDisplay(), 3000); + // sending event to trigger hover computation + editorTextWidget.getShell().forceActive(); + editorTextWidget.getShell().setActive(); + editorTextWidget.getShell().setFocus(); + editorTextWidget.getShell().getDisplay().wake(); + + ITextViewer viewer= (ITextViewer) new Accessor(editor, AbstractTextEditor.class).invoke("getSourceViewer", new Object[0]); + + ITextOperationTarget textOperationTarget = (ITextOperationTarget)viewer; + assertTrue(textOperationTarget.canDoOperation(ISourceViewer.INFORMATION)); + textOperationTarget.doOperation(ISourceViewer.INFORMATION); + + AbstractInformationControlManager informationControlManager= (AbstractInformationControlManager) new Accessor(viewer, SourceViewer.class).get("fInformationPresenter"); + // retrieving hover content + new DisplayHelper() { + @Override + protected boolean condition() { + return getShowInformationData(informationControlManager) != null; + } + }.waitForCondition(editorTextWidget.getDisplay(), 6000); + return informationControlManager; + } +} diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java index 3ac10296e24..2c14aa78c01 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2017 Red Hat Inc. and others. + * Copyright (c) 2016, 2021 Red Hat Inc. and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -20,12 +20,15 @@ package org.eclipse.ui.internal.genericeditor; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Queue; import java.util.Set; +import java.util.stream.Collectors; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; @@ -37,22 +40,37 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.AbstractReusableInformationControlCreator; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentPartitioningListener; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlCreator; +import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.ITextHoverExtension; +import org.eclipse.jface.text.ITextHoverExtension2; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContentAssistant; +import org.eclipse.jface.text.information.IInformationPresenter; +import org.eclipse.jface.text.information.IInformationProvider; +import org.eclipse.jface.text.information.IInformationProviderExtension; +import org.eclipse.jface.text.information.IInformationProviderExtension2; +import org.eclipse.jface.text.information.InformationPresenter; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; import org.eclipse.jface.text.quickassist.QuickAssistAssistant; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; import org.eclipse.ui.internal.editors.text.EditorsPlugin; import org.eclipse.ui.internal.genericeditor.folding.DefaultFoldingReconciler; +import org.eclipse.ui.internal.genericeditor.hover.CompositeInformationControlCreator; import org.eclipse.ui.internal.genericeditor.hover.CompositeTextHover; import org.eclipse.ui.internal.genericeditor.markers.MarkerResoltionQuickAssistProcessor; import org.eclipse.ui.texteditor.ITextEditor; @@ -271,6 +289,109 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewe return targets; } + @Override + public IInformationPresenter getInformationPresenter(ISourceViewer sourceViewer) { + // Register information provider + List<ITextHover> hovers = GenericEditorPlugin.getDefault().getHoverRegistry().getAvailableHovers(sourceViewer, + editor, getContentTypes(sourceViewer.getDocument())); + + InformationPresenter presenter = new InformationPresenter(new CompositeInformationControlCreator(hovers)); + // By default the InformationPresented is set to take the focus when visible, + // which makes the Browser to overtake all the focus/mouse etc. control over the + // 'org.eclipse.jface.text.information.InformationPresenter.Closer`. + // As we want to make t possible to close the information presenter by clicking + // outside of the information control or resizing the editor etc. - we need to + // disable such focus overtake by calling `takesFocusWhenVisible(false)` on the + // presenter. + // + presenter.takesFocusWhenVisible(false); + presenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); + + IInformationProvider provider = new ExtensionBaseInformationProvider(hovers); + // Register information provider + if (hovers != null && !hovers.isEmpty()) { + for (String contentType : getConfiguredContentTypes(sourceViewer)) { + presenter.setInformationProvider(provider, contentType); + } + } + + // sizes: see org.eclipse.jface.text.TextViewer.TEXT_HOVER_*_CHARS + presenter.setSizeConstraints(100, 12, false, true); + return presenter; + } + + class ExtensionBaseInformationProvider + implements IInformationProvider, IInformationProviderExtension, IInformationProviderExtension2 { + List<ITextHover> fHovers; + private LinkedHashMap<ITextHover, Object> currentHovers; + + ExtensionBaseInformationProvider(List<ITextHover> hovers) { + this.fHovers = hovers; + } + + @Override + public Object getInformation2(ITextViewer textViewer, IRegion subject) { + currentHovers = new LinkedHashMap<>(); + for (ITextHover hover : this.fHovers) { + Object res = hover instanceof ITextHoverExtension2 + ? ((ITextHoverExtension2) hover).getHoverInfo2(textViewer, subject) + : hover.getHoverInfo(textViewer, subject); + if (res != null) { + currentHovers.put(hover, res); + } + } + if (currentHovers.isEmpty()) { + return null; + } else if (currentHovers.size() == 1) { + return currentHovers.values().iterator().next(); + } + return currentHovers; + } + + @Override + public IRegion getSubject(ITextViewer textViewer, int offset) { + IRegion res = null; + for (ITextHover hover : this.fHovers) { + IRegion region = hover.getHoverRegion(textViewer, offset); + if (region != null) { + if (res == null) { + res = region; + } else { + int startOffset = Math.max(res.getOffset(), region.getOffset()); + int endOffset = Math.min(res.getOffset() + res.getLength(), + region.getOffset() + region.getLength()); + res = new Region(startOffset, endOffset - startOffset); + } + } + } + return res; + } + + @Override + public String getInformation(ITextViewer textViewer, IRegion subject) { + return this.fHovers.stream().map(hover -> hover.getHoverInfo(textViewer, subject)).filter(Objects::nonNull) + .collect(Collectors.joining("\n")); //$NON-NLS-1$ + } + + @Override + public IInformationControlCreator getInformationPresenterControlCreator() { + if (this.currentHovers == null || this.currentHovers.isEmpty()) { + return null; + } else if (currentHovers.size() == 1) { + ITextHover hover = this.currentHovers.keySet().iterator().next(); + return hover instanceof ITextHoverExtension ? ((ITextHoverExtension) hover).getHoverControlCreator() + : new AbstractReusableInformationControlCreator() { + @Override + protected IInformationControl doCreateInformationControl(Shell parent) { + return new DefaultInformationControl(parent); + }; + }; + } else { + return new CompositeInformationControlCreator(new ArrayList<>(this.currentHovers.keySet())); + } + } + } + /** * Set content-types that will be considered is no content-type can be deduced * from the document (eg document is not backed by a FileBuffer) |