Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMickael Istria2016-11-19 17:02:18 +0000
committerMickael Istria - away until January 11th2017-01-17 15:38:17 +0000
commitaa9773529d30c379ea5c0b25683e698ce2fb4574 (patch)
tree425fa5db3bce7f1c79c5e460290071f135bcfb01
parentd77b078388163500961bdab34c0248806eb5c208 (diff)
downloadeclipse.platform.text-aa9773529d30c379ea5c0b25683e698ce2fb4574.tar.gz
eclipse.platform.text-aa9773529d30c379ea5c0b25683e698ce2fb4574.tar.xz
eclipse.platform.text-aa9773529d30c379ea5c0b25683e698ce2fb4574.zip
Bug 251156 - Asynchronous content assist
This provides a new AsyncContentAssistant, performaing computation and presentation of content assist proposals (and related operations) asynchronously whenever possible. Part of this change is also internally allowing the parent ContentAssistant to support multiple IConteantAssistProcessors (although this is not exposed through public API). The AsyncContentAssistant does expose the manipulation of multiple contentAssistProcessors though. Some fields and methods where also made more visible (usually from private to protected or package, to maximize the ability to reuse existing code from the current synchronous framework, in order to make the feature easier to write, and also to provide a consistent experience with aysnc and sync content assist. Change-Id: I3e80c6422eaababc165e955ddeb3539909bfcb9f Signed-off-by: Mickael Istria <mistria@redhat.com>
-rw-r--r--org.eclipse.jface.text/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.jface.text/pom.xml2
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncCompletionProposalPopup.java256
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncContentAssistSubjectControlAdapter.java28
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncContentAssistant.java33
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java140
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.java3
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistSubjectControlAdapter.java9
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java81
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties1
-rw-r--r--org.eclipse.ui.genericeditor.examples/plugin.xml4
-rw-r--r--org.eclipse.ui.genericeditor.examples/src/org/eclipse/ui/genericeditor/examples/dotproject/NaturesAndProjectsContentAssistProcessor.java2
-rw-r--r--org.eclipse.ui.genericeditor.tests/plugin.xml4
-rw-r--r--org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/CompletionTest.java24
-rw-r--r--org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestSuite.java6
-rw-r--r--org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestUtils.java10
-rw-r--r--org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java15
-rw-r--r--org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/BarContentAssistProcessor.java6
-rw-r--r--org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/LongRunningBarContentAssistProcessor.java67
-rw-r--r--org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java36
-rw-r--r--org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java19
21 files changed, 613 insertions, 136 deletions
diff --git a/org.eclipse.jface.text/META-INF/MANIFEST.MF b/org.eclipse.jface.text/META-INF/MANIFEST.MF
index 33bf34d1f..013a8d770 100644
--- a/org.eclipse.jface.text/META-INF/MANIFEST.MF
+++ b/org.eclipse.jface.text/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.jface.text
-Bundle-Version: 3.11.100.qualifier
+Bundle-Version: 3.12.0.qualifier
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Export-Package:
@@ -36,3 +36,4 @@ Require-Bundle:
org.eclipse.jface;bundle-version="[3.5.0,4.0.0)"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.ibm.icu.text
+Bundle-ActivationPolicy: lazy
diff --git a/org.eclipse.jface.text/pom.xml b/org.eclipse.jface.text/pom.xml
index dd83c4fd7..d3d784d7b 100644
--- a/org.eclipse.jface.text/pom.xml
+++ b/org.eclipse.jface.text/pom.xml
@@ -18,6 +18,6 @@
</parent>
<groupId>org.eclipse.jface</groupId>
<artifactId>org.eclipse.jface.text</artifactId>
- <version>3.11.100-SNAPSHOT</version>
+ <version>3.12.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncCompletionProposalPopup.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncCompletionProposalPopup.java
new file mode 100644
index 000000000..f46384c11
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncCompletionProposalPopup.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Mickael Istria (Red Hat Inc.) - [251156] async content assist
+ *******************************************************************************/
+package org.eclipse.jface.text.contentassist;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.osgi.util.NLS;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextUtilities;
+
+/**
+ * This is the controller for the completion proposal list, which is used
+ * by the {@link AsyncContentAssistant}. It is aimed at orchestrating all operations
+ * from {@link IContentAssistProcessor} asynchronously and to provide a good user
+ * experience, including reporting and eye-candies.
+ */
+class AsyncCompletionProposalPopup extends CompletionProposalPopup {
+
+ private static final int MAX_WAIT_IN_MS= 50; // TODO make it a preference
+ private List<CompletableFuture<List<ICompletionProposal>>> fFutures;
+
+ private static final class ComputingProposal implements ICompletionProposal, ICompletionProposalExtension {
+
+ private final int fOffset;
+ private final int fSize;
+ private int fRemaining;
+
+ public ComputingProposal(int offset, int size) {
+ fSize= size;
+ fRemaining = size;
+ fOffset = offset;
+ }
+
+ @Override
+ public void apply(IDocument document) {
+ // Nothing to do
+ }
+
+ @Override
+ public Point getSelection(IDocument document) {
+ return new Point(fOffset, 0);
+ }
+
+ @Override
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ @Override
+ public Image getImage() {
+ return null;
+ }
+
+ @Override
+ public String getDisplayString() {
+ return NLS.bind(JFaceTextMessages.getString("CompletionProposalPopup.computing"), Integer.valueOf(fSize - fRemaining), Integer.valueOf(fSize)); //$NON-NLS-1$
+ }
+
+ @Override
+ public String getAdditionalProposalInfo() {
+ return null;
+ }
+
+ @Override
+ public void apply(IDocument document, char trigger, int offset) {
+ // Nothing to do
+ }
+
+ @Override
+ public boolean isValidFor(IDocument document, int offset) {
+ return false;
+ }
+
+ @Override
+ public char[] getTriggerCharacters() {
+ return null;
+ }
+
+ @Override
+ public int getContextInformationPosition() {
+ return -1;
+ }
+
+ public void setRemaining(int size) {
+ this.fRemaining = size;
+ }
+ }
+
+ public AsyncCompletionProposalPopup(ContentAssistant contentAssistant, IContentAssistSubjectControl contentAssistSubjectControl, AdditionalInfoController infoController) {
+ super(contentAssistant, contentAssistSubjectControl, infoController);
+ }
+
+ public AsyncCompletionProposalPopup(ContentAssistant contentAssistant, ITextViewer viewer, AdditionalInfoController infoController) {
+ super(contentAssistant, viewer, infoController);
+ }
+
+ /**
+ * This methods differs from its super as it will show the list of proposals that
+ * gets augmented as the {@link IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)}
+ * complete. All computations operation happen in a non-UI Thread so they're not blocking UI.
+ */
+ @Override
+ public String showProposals(boolean autoActivated) {
+ if (fKeyListener == null)
+ fKeyListener= new ProposalSelectionListener();
+
+ final Control control= fContentAssistSubjectControlAdapter.getControl();
+
+ if (!Helper.okToUse(fProposalShell) && control != null && !control.isDisposed()) {
+ // add the listener before computing the proposals so we don't move the caret
+ // when the user types fast.
+ fContentAssistSubjectControlAdapter.addKeyListener(fKeyListener);
+
+ fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+ fFilterOffset= fInvocationOffset;
+ fLastCompletionOffset= fFilterOffset;
+ // start invocation of processors as Futures, and make them populate the proposals upon completion
+ List<ICompletionProposal> computedProposals = Collections.synchronizedList(new ArrayList<>());
+ fFutures= buildCompletionFuturesOrJobs(fInvocationOffset);
+ List<CompletableFuture<Void>> populateFutures = new ArrayList<>(fFutures.size());
+ for (CompletableFuture<List<ICompletionProposal>> future : fFutures) {
+ populateFutures.add(future.thenAccept(proposals ->
+ computedProposals.addAll(proposals)
+ ));
+ }
+
+ long requestBeginningTimestamp = System.currentTimeMillis();
+ long stillRemainingThreeshold = MAX_WAIT_IN_MS;
+ for (CompletableFuture<List<ICompletionProposal>> future : fFutures) {
+ try {
+ future.get(stillRemainingThreeshold, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | ExecutionException | InterruptedException ex) {
+ // future failed or took more time than we want to wait
+ }
+ stillRemainingThreeshold = MAX_WAIT_IN_MS - (System.currentTimeMillis() - requestBeginningTimestamp);
+ if (stillRemainingThreeshold < 0) {
+ // we already spent too much time (more than MAX_WAIT_IN_MS), stop waiting.
+ break;
+ }
+ }
+ fComputedProposals = computedProposals;
+ if (stillRemainingThreeshold > 0) { // everything ready in time, go synchronous
+ int count= (computedProposals == null ? 0 : computedProposals.size());
+ if (count == 0 && hideWhenNoProposals(autoActivated))
+ return null;
+
+ if (count == 1 && !autoActivated && canAutoInsert(computedProposals.get(0))) {
+ insertProposal(computedProposals.get(0), (char) 0, 0, fInvocationOffset);
+ hide();
+ } else {
+ createProposalSelector();
+ setProposals(computedProposals, false);
+ displayProposals();
+ }
+ } else { // processors took too much time, go asynchronous
+ createProposalSelector();
+ ComputingProposal computingProposal= new ComputingProposal(fInvocationOffset, fFutures.size());
+ computedProposals.add(0, computingProposal);
+ fComputedProposals = computedProposals;
+ setProposals(fComputedProposals, false);
+ Set<CompletableFuture<Void>> remaining = Collections.synchronizedSet(new HashSet<>(populateFutures));
+ for (CompletableFuture<Void> populateFuture : populateFutures) {
+ populateFuture.thenRun(() -> {
+ remaining.removeIf(CompletableFuture::isDone);
+ computingProposal.setRemaining(remaining.size());
+ if (remaining.isEmpty()) {
+ computedProposals.remove(computingProposal);
+ }
+ List<ICompletionProposal> newProposals = new ArrayList<>(computedProposals);
+ fComputedProposals = newProposals;
+ Display.getDefault().asyncExec(() -> {
+ setProposals(newProposals, false);
+ displayProposals();
+ });
+ });
+ }
+ displayProposals();
+ }
+ } else {
+ fLastCompletionOffset= fFilterOffset;
+ handleRepeatedInvocation();
+ }
+
+ return getErrorMessage();
+ }
+
+ @Override
+ public void hide() {
+ super.hide();
+ if (fFutures != null) {
+ for (Future<?> future : fFutures) {
+ future.cancel(true);
+ }
+ }
+ }
+
+ protected List<CompletableFuture<List<ICompletionProposal>>> buildCompletionFuturesOrJobs(int invocationOffset) {
+ Set<IContentAssistProcessor> processors = null;
+ try {
+ processors= fContentAssistant.getContentAssistProcessors(getTokenContentType(invocationOffset));
+ } catch (BadLocationException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ if (processors == null) {
+ return Collections.emptyList();
+ }
+ List<CompletableFuture<List<ICompletionProposal>>> futures = new ArrayList<>(processors.size());
+ for (IContentAssistProcessor processor : processors) {
+ futures.add(CompletableFuture.supplyAsync(() ->
+ Arrays.asList(processor.computeCompletionProposals(fViewer, invocationOffset))
+ ));
+ }
+ return futures;
+ }
+
+ private String getTokenContentType(int invocationOffset) throws BadLocationException {
+ if (fContentAssistSubjectControl != null) {
+ IDocument document= fContentAssistSubjectControl.getDocument();
+ if (document != null) {
+ return TextUtilities.getContentType(document, fContentAssistant.getDocumentPartitioning(), invocationOffset, true);
+ }
+ } else {
+ return TextUtilities.getContentType(fViewer.getDocument(), fContentAssistant.getDocumentPartitioning(), invocationOffset, true);
+ }
+ return IDocument.DEFAULT_CONTENT_TYPE;
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncContentAssistSubjectControlAdapter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncContentAssistSubjectControlAdapter.java
new file mode 100644
index 000000000..d0d3cfded
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncContentAssistSubjectControlAdapter.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Mickael Istria (Red Hat Inc.) - [251156] async content assist
+ *******************************************************************************/
+package org.eclipse.jface.text.contentassist;
+
+import org.eclipse.jface.text.ITextViewer;
+
+class AsyncContentAssistSubjectControlAdapter extends ContentAssistSubjectControlAdapter {
+
+ public AsyncContentAssistSubjectControlAdapter(ITextViewer viewer) {
+ super(viewer);
+ }
+
+ @Override
+ CompletionProposalPopup createCompletionProposalPopup(ContentAssistant contentAssistant, AdditionalInfoController controller) {
+ if (fContentAssistSubjectControl != null)
+ return new AsyncCompletionProposalPopup(contentAssistant, fContentAssistSubjectControl, controller);
+ return new AsyncCompletionProposalPopup(contentAssistant, fViewer, controller);
+ }
+
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncContentAssistant.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncContentAssistant.java
new file mode 100644
index 000000000..22aa3caeb
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AsyncContentAssistant.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Mickael Istria (Red Hat Inc.) - [251156] async content assist
+ *******************************************************************************/
+package org.eclipse.jface.text.contentassist;
+
+import org.eclipse.jface.text.ITextViewer;
+
+/**
+ * A content assistant allowing multiple {@link IContentAssistProcessor}s and invoking their methods
+ * asynchronously whenever possible.
+ * @since 3.12
+ */
+public class AsyncContentAssistant extends ContentAssistant {
+
+ @Override
+ public void addContentAssistProcessor(IContentAssistProcessor processor, String contentType) {
+ super.addContentAssistProcessor(processor, contentType);
+ }
+
+ @Override
+ public void install(ITextViewer textViewer) {
+ fViewer= textViewer;
+ fContentAssistSubjectControlAdapter= new AsyncContentAssistSubjectControlAdapter(fViewer);
+ install();
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
index 7a88390db..365dc2408 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
@@ -11,11 +11,13 @@
* Marcel Bruch, bruch@cs.tu-darmstadt.de - [content assist] Allow to re-sort proposals - https://bugs.eclipse.org/bugs/show_bug.cgi?id=350991
* Terry Parker, tparker@google.com - Protect against poorly behaved completion proposers - http://bugs.eclipse.org/429925
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 493649
+ * Mickael Istria (Red Hat Inc.) - [251156] Allow multiple contentAssitProviders internally & inheritance
*******************************************************************************/
package org.eclipse.jface.text.contentassist;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import org.eclipse.osgi.util.TextProcessor;
@@ -214,7 +216,7 @@ class CompletionProposalPopup implements IContentAssistListener {
}
}
- private final class ProposalSelectionListener implements KeyListener {
+ final class ProposalSelectionListener implements KeyListener {
@Override
public void keyPressed(KeyEvent e) {
if (!Helper.okToUse(fProposalShell))
@@ -267,33 +269,33 @@ class CompletionProposalPopup implements IContentAssistListener {
/** The associated text viewer. */
- private ITextViewer fViewer;
+ ITextViewer fViewer;
/** The associated content assistant. */
- private final ContentAssistant fContentAssistant;
+ final ContentAssistant fContentAssistant;
/** The used additional info controller, or <code>null</code> if none. */
private final AdditionalInfoController fAdditionalInfoController;
/** The closing strategy for this completion proposal popup. */
private final PopupCloser fPopupCloser= new PopupCloser();
/** The popup shell. */
- private Shell fProposalShell;
+ Shell fProposalShell;
/** The proposal table. */
private Table fProposalTable;
/** Indicates whether a completion proposal is being inserted. */
private boolean fInserting= false;
/** The key listener to control navigation. */
- private ProposalSelectionListener fKeyListener;
+ ProposalSelectionListener fKeyListener;
/** List of document events used for filtering proposals. */
private final List<DocumentEvent> fDocumentEvents= new ArrayList<>();
/** Listener filling the document event queue. */
private IDocumentListener fDocumentListener;
/** The filter list of proposals. */
- private ICompletionProposal[] fFilteredProposals;
+ private List<ICompletionProposal> fFilteredProposals;
/** The computed list of proposals. */
- private ICompletionProposal[] fComputedProposals;
+ List<ICompletionProposal> fComputedProposals;
/** The offset for which the proposals have been computed. */
- private int fInvocationOffset;
+ int fInvocationOffset;
/** The offset for which the computed proposals have been filtered. */
- private int fFilterOffset;
+ int fFilterOffset;
/**
* The most recently selected proposal.
* @since 3.0
@@ -305,14 +307,14 @@ class CompletionProposalPopup implements IContentAssistListener {
*
* @since 3.0
*/
- private IContentAssistSubjectControl fContentAssistSubjectControl;
+ IContentAssistSubjectControl fContentAssistSubjectControl;
/**
* The content assist subject control adapter.
* This replaces <code>fViewer</code>
*
* @since 3.0
*/
- private final ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
+ final ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
/**
* Remembers the size for this completion proposal popup.
* @since 3.0
@@ -348,7 +350,7 @@ class CompletionProposalPopup implements IContentAssistListener {
return;
int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
- ICompletionProposal[] proposals= null;
+ List<ICompletionProposal> proposals= null;
try {
if (offset > -1) {
DocumentEvent event= TextUtilities.mergeProcessedDocumentEvents(fDocumentEvents);
@@ -360,7 +362,7 @@ class CompletionProposalPopup implements IContentAssistListener {
}
fFilterOffset= offset;
- if (proposals != null && proposals.length > 0)
+ if (proposals != null && proposals.size() > 0)
setProposals(proposals, fIsFilteredSubset);
else
hide();
@@ -391,7 +393,7 @@ class CompletionProposalPopup implements IContentAssistListener {
*
* @since 3.2
*/
- private int fLastCompletionOffset;
+ int fLastCompletionOffset;
/**
* The (reusable) empty proposal.
*
@@ -418,7 +420,7 @@ class CompletionProposalPopup implements IContentAssistListener {
*
* @since 3.8
*/
- private ICompletionProposalSorter fSorter;
+ ICompletionProposalSorter fSorter;
/**
* Set to true by {@link #computeProposals(int)} when initial sorting is performed on the
@@ -426,7 +428,7 @@ class CompletionProposalPopup implements IContentAssistListener {
*
* @since 3.11
*/
- private boolean fIsInitialSort;
+ boolean fIsInitialSort;
/**
* Creates a new completion proposal popup for the given elements.
@@ -486,12 +488,12 @@ class CompletionProposalPopup implements IContentAssistListener {
fLastCompletionOffset= fFilterOffset;
fComputedProposals= computeProposals(fInvocationOffset);
- int count= (fComputedProposals == null ? 0 : fComputedProposals.length);
+ int count= (fComputedProposals == null ? 0 : fComputedProposals.size());
if (count == 0 && hideWhenNoProposals(autoActivated))
return;
- if (count == 1 && !autoActivated && canAutoInsert(fComputedProposals[0])) {
- insertProposal(fComputedProposals[0], (char) 0, 0, fInvocationOffset);
+ if (count == 1 && !autoActivated && canAutoInsert(fComputedProposals.get(0))) {
+ insertProposal(fComputedProposals.get(0), (char) 0, 0, fInvocationOffset);
hide();
} else {
createProposalSelector();
@@ -516,7 +518,7 @@ class CompletionProposalPopup implements IContentAssistListener {
* @return <code>false</code> if an empty list should be displayed, <code>true</code> otherwise
* @since 3.2
*/
- private boolean hideWhenNoProposals(boolean autoActivated) {
+ boolean hideWhenNoProposals(boolean autoActivated) {
if (autoActivated || !fContentAssistant.isShowEmptyList()) {
if (!autoActivated) {
Control control= fContentAssistSubjectControlAdapter.getControl();
@@ -535,7 +537,7 @@ class CompletionProposalPopup implements IContentAssistListener {
*
* @since 3.2
*/
- private void handleRepeatedInvocation() {
+ void handleRepeatedInvocation() {
if (fContentAssistant.isRepeatedInvocationMode()) {
fComputedProposals= computeProposals(fFilterOffset);
setProposals(fComputedProposals, false);
@@ -550,15 +552,15 @@ class CompletionProposalPopup implements IContentAssistListener {
* @param offset the offset
* @return the completion proposals available at this offset
*/
- private ICompletionProposal[] computeProposals(int offset) {
- ICompletionProposal[] proposals;
+ private List<ICompletionProposal> computeProposals(int offset) {
+ List<ICompletionProposal> proposals;
if (fContentAssistSubjectControl != null) {
- proposals= fContentAssistant.computeCompletionProposals(fContentAssistSubjectControl, offset);
+ proposals= Arrays.asList(fContentAssistant.computeCompletionProposals(fContentAssistSubjectControl, offset));
} else {
- proposals= fContentAssistant.computeCompletionProposals(fViewer, offset);
+ proposals= Arrays.asList(fContentAssistant.computeCompletionProposals(fViewer, offset));
}
if (proposals == null)
- return new ICompletionProposal[] {};
+ return Collections.emptyList();
if (fSorter != null) {
sortProposals(proposals);
fIsInitialSort= true;
@@ -571,14 +573,14 @@ class CompletionProposalPopup implements IContentAssistListener {
*
* @return the error message
*/
- private String getErrorMessage() {
+ String getErrorMessage() {
return fContentAssistant.getErrorMessage();
}
/**
* Creates the proposal selector.
*/
- private void createProposalSelector() {
+ void createProposalSelector() {
if (Helper.okToUse(fProposalShell))
return;
@@ -841,8 +843,8 @@ class CompletionProposalPopup implements IContentAssistListener {
TableItem item= (TableItem) event.item;
int index= fProposalTable.indexOf(item);
- if (0 <= index && index < fFilteredProposals.length) {
- ICompletionProposal current= fFilteredProposals[index];
+ if (0 <= index && index < fFilteredProposals.size()) {
+ ICompletionProposal current= fFilteredProposals.get(index);
String displayString;
StyleRange[] styleRanges= null;
@@ -909,9 +911,9 @@ class CompletionProposalPopup implements IContentAssistListener {
return null;
int i= fProposalTable.getSelectionIndex();
- if (fFilteredProposals == null || i < 0 || i >= fFilteredProposals.length)
+ if (fFilteredProposals == null || i < 0 || i >= fFilteredProposals.size())
return null;
- return fFilteredProposals[i];
+ return fFilteredProposals.get(i);
}
/**
@@ -937,7 +939,7 @@ class CompletionProposalPopup implements IContentAssistListener {
* @param offset the offset
* @since 2.1
*/
- private void insertProposal(ICompletionProposal p, char trigger, int stateMask, final int offset) {
+ void insertProposal(ICompletionProposal p, char trigger, int stateMask, final int offset) {
fInserting= true;
IRewriteTarget target= null;
@@ -1140,20 +1142,17 @@ class CompletionProposalPopup implements IContentAssistListener {
* not cleared, but the proposals that are not in the passed array
* are removed from the displayed set
*/
- private void setProposals(ICompletionProposal[] proposals, boolean isFilteredSubset) {
- ICompletionProposal[] oldProposals= fFilteredProposals;
+ void setProposals(List<ICompletionProposal> proposals, boolean isFilteredSubset) {
+ List<ICompletionProposal> oldProposals= fFilteredProposals;
ICompletionProposal oldProposal= getSelectedProposal(); // may trigger filtering and a reentrant call to setProposals()
if (oldProposals != fFilteredProposals) // reentrant call was first - abort
return;
if (Helper.okToUse(fProposalTable)) {
- if (oldProposal instanceof ICompletionProposalExtension2 && fViewer != null)
- ((ICompletionProposalExtension2) oldProposal).unselected(fViewer);
-
- if (proposals == null || proposals.length == 0) {
+ if (proposals == null || proposals.isEmpty()) {
fEmptyProposal.fOffset= fFilterOffset;
fEmptyProposal.fDisplayString= fEmptyMessage != null ? fEmptyMessage : JFaceTextMessages.getString("CompletionProposalPopup.no_proposals"); //$NON-NLS-1$
- proposals= new ICompletionProposal[] { fEmptyProposal };
+ proposals= Collections.singletonList(fEmptyProposal);
}
if (fSorter != null && !fIsInitialSort) {
@@ -1162,7 +1161,14 @@ class CompletionProposalPopup implements IContentAssistListener {
fIsInitialSort= false;
fFilteredProposals= proposals;
- final int newLen= proposals.length;
+ int index = fFilteredProposals.indexOf(oldProposal);
+ if (index == -1) {
+ index = 0;
+ if (oldProposal instanceof ICompletionProposalExtension2 && fViewer != null) {
+ ((ICompletionProposalExtension2) oldProposal).unselected(fViewer);
+ }
+ }
+ final int newLen= proposals.size();
fProposalTable.clearAll();
fProposalTable.setItemCount(newLen);
@@ -1172,7 +1178,7 @@ class CompletionProposalPopup implements IContentAssistListener {
if ((newLocation.x < currentLocation.x && newLocation.y == currentLocation.y) || newLocation.y < currentLocation.y)
fProposalShell.setLocation(newLocation);
- selectProposal(0, false);
+ selectProposal(index, false);
}
}
@@ -1201,7 +1207,7 @@ class CompletionProposalPopup implements IContentAssistListener {
* Displays this popup and install the additional info controller, so that additional info
* is displayed when a proposal is selected and additional info is available.
*/
- private void displayProposals() {
+ void displayProposals() {
if (!Helper.okToUse(fProposalShell) || !Helper.okToUse(fProposalTable))
return;
@@ -1392,7 +1398,7 @@ class CompletionProposalPopup implements IContentAssistListener {
return;
}
- ICompletionProposal proposal= fFilteredProposals[index];
+ ICompletionProposal proposal= fFilteredProposals.get(index);
if (proposal instanceof ICompletionProposalExtension2 && fViewer != null)
((ICompletionProposalExtension2) proposal).selected(fViewer, smartToggle);
@@ -1473,7 +1479,7 @@ class CompletionProposalPopup implements IContentAssistListener {
* @return the set of filtered proposals
* @since 3.0
*/
- private ICompletionProposal[] computeFilteredProposals(int offset, DocumentEvent event) {
+ private List<ICompletionProposal> computeFilteredProposals(int offset, DocumentEvent event) {
if (offset == fInvocationOffset && event == null) {
fIsFilteredSubset= false;
@@ -1488,7 +1494,7 @@ class CompletionProposalPopup implements IContentAssistListener {
return fComputedProposals;
}
- ICompletionProposal[] proposals;
+ List<ICompletionProposal> proposals;
if (offset < fFilterOffset) {
proposals= fComputedProposals;
fIsFilteredSubset= false;
@@ -1503,25 +1509,25 @@ class CompletionProposalPopup implements IContentAssistListener {
}
IDocument document= fContentAssistSubjectControlAdapter.getDocument();
- int length= proposals.length;
- List<Object> filtered= new ArrayList<>(length);
- for (int i= 0; i < length; i++) {
+ int length= proposals.size();
+ List<ICompletionProposal> filtered= new ArrayList<>(length);
+ for (ICompletionProposal proposal : proposals) {
- if (proposals[i] instanceof ICompletionProposalExtension2) {
+ if (proposal instanceof ICompletionProposalExtension2) {
- ICompletionProposalExtension2 p= (ICompletionProposalExtension2) proposals[i];
+ ICompletionProposalExtension2 p= (ICompletionProposalExtension2) proposal;
try {
if (p.validate(document, offset, event))
- filtered.add(p);
+ filtered.add(proposal);
} catch (RuntimeException e) {
// Make sure that poorly behaved completion proposers do not break filtering.
}
- } else if (proposals[i] instanceof ICompletionProposalExtension) {
+ } else if (proposal instanceof ICompletionProposalExtension) {
- ICompletionProposalExtension p= (ICompletionProposalExtension) proposals[i];
+ ICompletionProposalExtension p= (ICompletionProposalExtension) proposal;
try {
if (p.isValidFor(document, offset))
- filtered.add(p);
+ filtered.add(proposal);
} catch (RuntimeException e) {
// Make sure that poorly behaved completion proposers do not break filtering.
}
@@ -1535,7 +1541,7 @@ class CompletionProposalPopup implements IContentAssistListener {
}
}
- return filtered.toArray(new ICompletionProposal[filtered.size()]);
+ return filtered;
}
/**
@@ -1558,7 +1564,7 @@ class CompletionProposalPopup implements IContentAssistListener {
* <code>false</code> otherwise
* @since 3.1
*/
- private boolean canAutoInsert(ICompletionProposal proposal) {
+ boolean canAutoInsert(ICompletionProposal proposal) {
if (fContentAssistant.isAutoInserting()) {
if (proposal instanceof ICompletionProposalExtension4) {
ICompletionProposalExtension4 ext= (ICompletionProposalExtension4) proposal;
@@ -1602,12 +1608,12 @@ class CompletionProposalPopup implements IContentAssistListener {
fLastCompletionOffset= fFilterOffset;
fFilteredProposals= computeProposals(fInvocationOffset);
- int count= (fFilteredProposals == null ? 0 : fFilteredProposals.length);
+ int count= (fFilteredProposals == null ? 0 : fFilteredProposals.size());
if (count == 0 && hideWhenNoProposals(false))
return;
- if (count == 1 && canAutoInsert(fFilteredProposals[0])) {
- insertProposal(fFilteredProposals[0], (char) 0, 0, fInvocationOffset);
+ if (count == 1 && canAutoInsert(fFilteredProposals.get(0))) {
+ insertProposal(fFilteredProposals.get(0), (char) 0, 0, fInvocationOffset);
hide();
} else {
ensureDocumentListenerInstalled();
@@ -1639,9 +1645,9 @@ class CompletionProposalPopup implements IContentAssistListener {
private boolean completeCommonPrefix() {
// 0: insert single proposals
- if (fFilteredProposals.length == 1) {
- if (canAutoInsert(fFilteredProposals[0])) {
- insertProposal(fFilteredProposals[0], (char) 0, 0, fFilterOffset);
+ if (fFilteredProposals.size() == 1) {
+ if (canAutoInsert(fFilteredProposals.get(0))) {
+ insertProposal(fFilteredProposals.get(0), (char) 0, 0, fFilterOffset);
hide();
return true;
}
@@ -1666,8 +1672,8 @@ class CompletionProposalPopup implements IContentAssistListener {
List<ICompletionProposal> wrongCase= new ArrayList<>();
boolean hasMixedProposals= hasMixedProposals();
- for (int i= 0; i < fFilteredProposals.length; i++) {
- ICompletionProposal proposal= fFilteredProposals[i];
+ for (int i= 0; i < fFilteredProposals.size(); i++) {
+ ICompletionProposal proposal= fFilteredProposals.get(i);
if (!(proposal instanceof ICompletionProposalExtension3))
return false;
@@ -1969,7 +1975,7 @@ class CompletionProposalPopup implements IContentAssistListener {
* @throws NullPointerException if no sorter has been set
* @since 3.8
*/
- private void sortProposals(final ICompletionProposal[] proposals) {
- Arrays.sort(proposals, fSorter::compare);
+ void sortProposals(final List<ICompletionProposal> proposals) {
+ proposals.sort(fSorter::compare);
}
}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.java
index 749c47163..ddad61d5f 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.java
@@ -8,6 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Anton Leherbauer (Wind River Systems) - [content assist][api] ContentAssistEvent should contain information about auto activation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=193728
+ * Mickael Istria (Red Hat Inc.) - [251156] Allow multiple contentAssitProviders internally & inheritance
*******************************************************************************/
package org.eclipse.jface.text.contentassist;
@@ -43,7 +44,7 @@ public final class ContentAssistEvent {
* @param ca the assistant
* @param proc the processor
*/
- ContentAssistEvent(ContentAssistant ca, IContentAssistProcessor proc) {
+ ContentAssistEvent(IContentAssistant ca, IContentAssistProcessor proc) {
this(ca, proc, false);
}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistSubjectControlAdapter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistSubjectControlAdapter.java
index eef3647bc..6a8dd155d 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistSubjectControlAdapter.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistSubjectControlAdapter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Mickael Istria (Red Hat Inc.) - [251156] Allow multiple contentAssitProviders internally & inheritance
*******************************************************************************/
package org.eclipse.jface.text.contentassist;
@@ -36,17 +37,17 @@ import org.eclipse.jface.text.contentassist.ContextInformationPopup.ContextFrame
*
* @since 3.0
*/
-final class ContentAssistSubjectControlAdapter implements IContentAssistSubjectControl {
+class ContentAssistSubjectControlAdapter implements IContentAssistSubjectControl {
/**
* The text viewer which is used as content assist subject control.
*/
- private ITextViewer fViewer;
+ protected ITextViewer fViewer;
/**
* The content assist subject control.
*/
- private IContentAssistSubjectControl fContentAssistSubjectControl;
+ protected IContentAssistSubjectControl fContentAssistSubjectControl;
/**
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java
index bc6a52a4f..3697b013e 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java
@@ -11,13 +11,15 @@
* Anton Leherbauer (Wind River Systems) - [content assist][api] ContentAssistEvent should contain information about auto activation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=193728
* Marcel Bruch, bruch@cs.tu-darmstadt.de - [content assist] Allow to re-sort proposals - https://bugs.eclipse.org/bugs/show_bug.cgi?id=350991
* John Glassmyer, jogl@google.com - catch Content Assist exceptions to protect navigation keys - http://bugs.eclipse.org/434901
+ * Mickael Istria (Red Hat Inc.) - [251156] Allow multiple contentAssitProviders internally & inheritance
*******************************************************************************/
package org.eclipse.jface.text.contentassist;
+import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Set;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
@@ -908,7 +910,7 @@ public class ContentAssistant implements IContentAssistant, IContentAssistantExt
private boolean fIsAutoInserting= false;
private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
- private Map<String, IContentAssistProcessor> fProcessors;
+ private Map<String, Set<IContentAssistProcessor>> fProcessors;
/**
* The partitioning.
@@ -924,7 +926,7 @@ public class ContentAssistant implements IContentAssistant, IContentAssistantExt
private Color fProposalSelectorBackground;
private Color fProposalSelectorForeground;
- private ITextViewer fViewer;
+ ITextViewer fViewer;
private String fLastErrorMessage;
private Closer fCloser;
@@ -964,7 +966,7 @@ public class ContentAssistant implements IContentAssistant, IContentAssistantExt
*
* @since 3.0
*/
- private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
+ ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
/**
* The dialog settings for the control's bounds.
*
@@ -1087,7 +1089,32 @@ public class ContentAssistant implements IContentAssistant, IContentAssistantExt
if (processor == null)
fProcessors.remove(contentType);
else
- fProcessors.put(contentType, processor);
+ fProcessors.put(contentType, Collections.singleton(processor));
+ }
+
+ /**
+ * Registers a given content assist processor for a particular content type. If there is already
+ * a processor registered for this type, it is kept and the new processor is appended to the list
+ * of processors for given content-type.
+ *
+ * @param processor The content-assist process to add
+ * @param contentType Document token content-type it applies to
+ * @since 3.12
+ */
+ protected void addContentAssistProcessor(IContentAssistProcessor processor, String contentType) {
+ Assert.isNotNull(contentType);
+
+ if (fProcessors == null)
+ fProcessors= new HashMap<>();
+
+ if (processor == null) {
+ fProcessors.remove(contentType);
+ } else {
+ if (!fProcessors.containsKey(contentType)) {
+ fProcessors.put(contentType, new LinkedHashSet<>());
+ }
+ fProcessors.get(contentType).add(processor);
+ }
}
/*
@@ -1098,7 +1125,28 @@ public class ContentAssistant implements IContentAssistant, IContentAssistantExt
if (fProcessors == null)
return null;
- return fProcessors.get(contentType);
+ Set<IContentAssistProcessor> res = fProcessors.get(contentType);
+ if (res == null || res.isEmpty()) {
+ return null;
+ } else {
+ return res.iterator().next(); // return first one although there might be multiple ones... TODO, consider an aggregator contentAssistProcessor to return here
+ }
+ }
+
+ /**
+ * @param contentType Document token content-type it applies to
+ * @return the available content-assist processors for provide token content-type.
+ * @since 3.12
+ */
+ protected Set<IContentAssistProcessor> getContentAssistProcessors(String contentType) {
+ if (fProcessors == null)
+ return null;
+
+ Set<IContentAssistProcessor> res = fProcessors.get(contentType);
+ if (res == null || res.isEmpty()) {
+ return null;
+ }
+ return res;
}
/**
@@ -1112,16 +1160,15 @@ public class ContentAssistant implements IContentAssistant, IContentAssistantExt
return ""; //$NON-NLS-1$
StringBuffer buf= new StringBuffer(5);
- Iterator<Entry<String, IContentAssistProcessor>> iter= fProcessors.entrySet().iterator();
- while (iter.hasNext()) {
- Entry<String, IContentAssistProcessor> entry= iter.next();
- IContentAssistProcessor processor= entry.getValue();
- char[] triggers= processor.getCompletionProposalAutoActivationCharacters();
- if (triggers != null)
- buf.append(triggers);
- triggers= processor.getContextInformationAutoActivationCharacters();
- if (triggers != null)
- buf.append(triggers);
+ for (Set<IContentAssistProcessor> processorsForContentType : fProcessors.values()) {
+ for (IContentAssistProcessor processor : processorsForContentType) {
+ char[] triggers= processor.getCompletionProposalAutoActivationCharacters();
+ if (triggers != null)
+ buf.append(triggers);
+ triggers= processor.getContextInformationAutoActivationCharacters();
+ if (triggers != null)
+ buf.append(triggers);
+ }
}
return buf.toString();
}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties
index 84f874a5a..73584bdc4 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties
@@ -19,5 +19,6 @@ ContentAssistant.assist_delay_timer_name=AutoAssist Delay
ContentAssistant.error_computing_completion=Error computing completion proposals.
ContentAssistant.error_computing_context=Error computing context information.
CompletionProposalPopup.no_proposals=no proposals
+CompletionProposalPopup.computing=Computing ({0}/{1})...
CompletionProposalPopup.error_retrieving_proposal=Error retrieving proposal text
CompletionProposalPopup.unexpected_error=Unexpected error while retrieving text for a content assistance proposal.
diff --git a/org.eclipse.ui.genericeditor.examples/plugin.xml b/org.eclipse.ui.genericeditor.examples/plugin.xml
index 245f1019d..f03f4b6b7 100644
--- a/org.eclipse.ui.genericeditor.examples/plugin.xml
+++ b/org.eclipse.ui.genericeditor.examples/plugin.xml
@@ -14,11 +14,11 @@
<extension
point="org.eclipse.core.contenttype.contentTypes">
<content-type
- base-type="org.eclipse.core.runtime.text"
+ base-type="org.eclipse.core.runtime.xml"
file-names=".project"
id="dotproject"
name="Eclipse .project"
- priority="normal">
+ priority="high">
</content-type>
</extension>
<extension
diff --git a/org.eclipse.ui.genericeditor.examples/src/org/eclipse/ui/genericeditor/examples/dotproject/NaturesAndProjectsContentAssistProcessor.java b/org.eclipse.ui.genericeditor.examples/src/org/eclipse/ui/genericeditor/examples/dotproject/NaturesAndProjectsContentAssistProcessor.java
index 37509080b..86f7848b0 100644
--- a/org.eclipse.ui.genericeditor.examples/src/org/eclipse/ui/genericeditor/examples/dotproject/NaturesAndProjectsContentAssistProcessor.java
+++ b/org.eclipse.ui.genericeditor.examples/src/org/eclipse/ui/genericeditor/examples/dotproject/NaturesAndProjectsContentAssistProcessor.java
@@ -26,7 +26,7 @@ public class NaturesAndProjectsContentAssistProcessor implements IContentAssistP
@Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
- String text = viewer.getTextWidget().getText();
+ String text = viewer.getDocument().get();
String natureTag= "<nature>";
String projectReferenceTag="<project>";
IWorkspace workspace = ResourcesPlugin.getWorkspace();
diff --git a/org.eclipse.ui.genericeditor.tests/plugin.xml b/org.eclipse.ui.genericeditor.tests/plugin.xml
index 0a869d463..192e1be82 100644
--- a/org.eclipse.ui.genericeditor.tests/plugin.xml
+++ b/org.eclipse.ui.genericeditor.tests/plugin.xml
@@ -17,6 +17,10 @@
class="org.eclipse.ui.genericeditor.tests.contributions.BarContentAssistProcessor"
contentType="org.eclipse.core.runtime.text">
</contentAssistProcessor>
+ <contentAssistProcessor
+ class="org.eclipse.ui.genericeditor.tests.contributions.LongRunningBarContentAssistProcessor"
+ contentType="org.eclipse.core.runtime.text">
+ </contentAssistProcessor>
</extension>
<extension
point="org.eclipse.ui.genericeditor.hoverProviders">
diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/CompletionTest.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/CompletionTest.java
index 8f7e69a6d..46e5bf4ed 100644
--- a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/CompletionTest.java
+++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/CompletionTest.java
@@ -11,6 +11,7 @@
package org.eclipse.ui.genericeditor.tests;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.HashSet;
@@ -32,6 +33,8 @@ import org.eclipse.swt.widgets.Widget;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.genericeditor.tests.contributions.BarContentAssistProcessor;
+import org.eclipse.ui.genericeditor.tests.contributions.LongRunningBarContentAssistProcessor;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.AbstractTextEditor;
@@ -76,14 +79,29 @@ public class CompletionTest {
ContentAssistAction action = (ContentAssistAction) editor.getAction(ITextEditorActionConstants.CONTENT_ASSIST);
action.update();
action.run();
+ GenericEditorTestUtils.waitAndDispatch(100);
Set<Shell> afterShell = new HashSet<>(Arrays.asList(Display.getDefault().getShells()));
afterShell.removeAll(beforeShell);
assertEquals("No completion", 1, afterShell.size());
Shell completionShell= afterShell.iterator().next();
Table completionProposalList = findCompletionSelectionControl(completionShell);
- assertEquals(1, completionProposalList.getItemCount());
- TableItem completionProposalItem = completionProposalList.getItem(0);
- assertEquals("s are good for a beer.", ((ICompletionProposal)completionProposalItem.getData()).getDisplayString());
+ // instantaneous
+ assertEquals(2, completionProposalList.getItemCount());
+ TableItem computingItem = completionProposalList.getItem(0);
+ assertTrue("Missing computing info entry", computingItem.getText().contains("Computing")); //$NON-NLS-1$ //$NON-NLS-2$
+ TableItem completionProposalItem = completionProposalList.getItem(1);
+ ICompletionProposal completionProposal = (ICompletionProposal)completionProposalItem.getData();
+ assertEquals(BarContentAssistProcessor.PROPOSAL, completionProposal .getDisplayString());
+ completionProposalList.setSelection(completionProposalItem);
+ GenericEditorTestUtils.waitAndDispatch(LongRunningBarContentAssistProcessor.DELAY + 100);
+ // asynchronous
+ assertEquals(2, completionProposalList.getItemCount());
+ completionProposalItem = completionProposalList.getItem(0);
+ assertEquals(BarContentAssistProcessor.PROPOSAL, ((ICompletionProposal)completionProposalItem.getData()).getDisplayString());
+ TableItem otherProposalItem = completionProposalList.getItem(1);
+ assertEquals(LongRunningBarContentAssistProcessor.PROPOSAL, ((ICompletionProposal)otherProposalItem.getData()).getDisplayString());
+ assertEquals("Addition of completion proposal should keep selection", completionProposal, completionProposalList.getSelection()[0].getData());
+
// TODO find a way to actually trigger completion and verify result against Editor content
// Assert.assertEquals("Completion didn't complete", "bars are good for a beer.", ((StyledText)editor.getAdapter(Control.class)).getText());
completionShell.close();
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 9e41f7e30..115f6005e 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
@@ -15,11 +15,6 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
-/**
- * Test Suite for org.eclipse.ui.editors.
- *
- * @since 3.0
- */
@RunWith(Suite.class)
@SuiteClasses({
CompletionTest.class,
@@ -28,4 +23,5 @@ import org.junit.runners.Suite.SuiteClasses;
})
public class GenericEditorTestSuite {
// see @SuiteClasses
+
}
diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestUtils.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestUtils.java
index 33caf13be..9d0562613 100644
--- a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestUtils.java
+++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/GenericEditorTestUtils.java
@@ -12,6 +12,8 @@ package org.eclipse.ui.genericeditor.tests;
import java.io.ByteArrayInputStream;
+import org.eclipse.swt.widgets.Display;
+
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
@@ -47,5 +49,13 @@ public class GenericEditorTestUtils {
public static IFile getFile(){
return file;
}
+
+ public static void waitAndDispatch(long milliseconds) {
+ long timeout = milliseconds; //ms
+ long start = System.currentTimeMillis();
+ while (start + timeout > System.currentTimeMillis()) {
+ Display.getDefault().readAndDispatch();
+ }
+ }
}
diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java
index 6d29a391f..f8fbd32da 100644
--- a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java
+++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java
@@ -24,7 +24,6 @@ import org.junit.Test;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.core.resources.IMarker;
@@ -96,7 +95,7 @@ public class HoverTest {
private Object getHoverData() throws Exception {
this.editor.selectAndReveal(2, 0);
- waitAndDispatch();
+ GenericEditorTestUtils.waitAndDispatch(1000);
// sending event to trigger hover computation
StyledText editorTextWidget = (StyledText) this.editor.getAdapter(Control.class);
editorTextWidget.getShell().forceActive();
@@ -112,7 +111,7 @@ public class HoverTest {
hoverEvent.doit = true;
editorTextWidget.notifyListeners(SWT.MouseHover, hoverEvent);
// Events need to be processed for hover listener to work correctly
- waitAndDispatch();
+ GenericEditorTestUtils.waitAndDispatch(1000);
// retrieving hover content
Method getSourceViewerMethod= AbstractTextEditor.class.getDeclaredMethod("getSourceViewer");
getSourceViewerMethod.setAccessible(true);
@@ -123,16 +122,8 @@ public class HoverTest {
Field informationField = AbstractInformationControlManager.class.getDeclaredField("fInformation");
informationField.setAccessible(true);
Object hoverData = informationField.get(hover);
- waitAndDispatch();
+ GenericEditorTestUtils.waitAndDispatch(1000);
return hoverData;
}
- private void waitAndDispatch() {
- long timeout = 1000; //ms
- long start = System.currentTimeMillis();
- while (start + timeout > System.currentTimeMillis()) {
- Display.getDefault().readAndDispatch();
- }
- }
-
}
diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/BarContentAssistProcessor.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/BarContentAssistProcessor.java
index 4d5f12ba4..7f6a14857 100644
--- a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/BarContentAssistProcessor.java
+++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/BarContentAssistProcessor.java
@@ -19,11 +19,13 @@ import org.eclipse.jface.text.contentassist.IContextInformationValidator;
public class BarContentAssistProcessor implements IContentAssistProcessor {
+ public static final String PROPOSAL = "s are good for a beer.";
+
@Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
- String text = viewer.getTextWidget().getText();
+ String text = viewer.getDocument().get();
if (text.length() >= 3 && text.substring(offset - 3, offset).equals("bar")) {
- String message = "s are good for a beer.";
+ String message = PROPOSAL;
CompletionProposal proposal = new CompletionProposal(message, offset, 0, message.length());
return new ICompletionProposal[] { proposal };
}
diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/LongRunningBarContentAssistProcessor.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/LongRunningBarContentAssistProcessor.java
new file mode 100644
index 000000000..b90252437
--- /dev/null
+++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/LongRunningBarContentAssistProcessor.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * - Mickael Istria (Red Hat Inc.)
+ *******************************************************************************/
+package org.eclipse.ui.genericeditor.tests.contributions;
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.CompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
+
+public class LongRunningBarContentAssistProcessor implements IContentAssistProcessor {
+
+ public static final String PROPOSAL = "s are also good for soft drink cocktails.";
+ public static final int DELAY = 2000;
+
+ @Override
+ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
+ try {
+ Thread.sleep(DELAY);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ String text = viewer.getDocument().get();
+ if (text.length() >= 3 && text.substring(offset - 3, offset).equals("bar")) {
+ String message = PROPOSAL;
+ CompletionProposal proposal = new CompletionProposal(message, offset, 0, message.length());
+ return new ICompletionProposal[] { proposal };
+ }
+ return new ICompletionProposal[0];
+ }
+
+ @Override
+ public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
+ return null;
+ }
+
+ @Override
+ public char[] getCompletionProposalAutoActivationCharacters() {
+ return null;
+ }
+
+ @Override
+ public char[] getContextInformationAutoActivationCharacters() {
+ return null;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return null;
+ }
+
+ @Override
+ public IContextInformationValidator getContextInformationValidator() {
+ return null;
+ }
+
+}
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java
index a81cecbf8..1ce07367e 100644
--- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java
@@ -17,8 +17,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.IRegistryChangeListener;
import org.eclipse.core.runtime.IStatus;
@@ -32,7 +34,6 @@ import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.source.ISourceViewer;
-import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.PlatformUI;
/**
@@ -80,12 +81,21 @@ public class ContentAssistProcessorRegistry {
/**
* @return whether the referenced contribution should contribute to the current editor.
*/
- public boolean isActive() {
- IEditorInput input = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getEditorInput();
- IContentTypeManager contentTypeManager= Platform.getContentTypeManager();
- for (IContentType currentContentType : contentTypeManager.findContentTypesFor(input.getName())) {
- if (currentContentType.isKindOf(targetContentType)) {
- return true;
+ public boolean isActive(ITextViewer viewer) {
+ String fileName = null;
+ if (viewer != null && viewer.getDocument() != null) {
+ IPath location = FileBuffers.getTextFileBufferManager().getTextFileBuffer(viewer.getDocument()).getLocation();
+ fileName = location.segment(location.segmentCount() - 1);
+ }
+ if (fileName == null) {
+ fileName = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getEditorInput().getName();
+ }
+ if (fileName != null) {
+ IContentTypeManager contentTypeManager= Platform.getContentTypeManager();
+ for (IContentType currentContentType : contentTypeManager.findContentTypesFor(fileName)) {
+ if (currentContentType.isKindOf(targetContentType)) {
+ return true;
+ }
}
}
return false;
@@ -93,7 +103,7 @@ public class ContentAssistProcessorRegistry {
@Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
- if (isActive()) {
+ if (isActive(viewer)) {
return getDelegate().computeCompletionProposals(viewer, offset);
}
return new ICompletionProposal[0];
@@ -101,7 +111,7 @@ public class ContentAssistProcessorRegistry {
@Override
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
- if (isActive()) {
+ if (isActive(viewer)) {
return getDelegate().computeContextInformation(viewer, offset);
}
return new IContextInformation[0];
@@ -109,7 +119,7 @@ public class ContentAssistProcessorRegistry {
@Override
public char[] getCompletionProposalAutoActivationCharacters() {
- if (isActive()) {
+ if (isActive(null)) {
return getDelegate().getCompletionProposalAutoActivationCharacters();
}
return null;
@@ -117,7 +127,7 @@ public class ContentAssistProcessorRegistry {
@Override
public char[] getContextInformationAutoActivationCharacters() {
- if (isActive()) {
+ if (isActive(null)) {
return getDelegate().getContextInformationAutoActivationCharacters();
}
return null;
@@ -125,7 +135,7 @@ public class ContentAssistProcessorRegistry {
@Override
public String getErrorMessage() {
- if (isActive()) {
+ if (isActive(null)) {
return getDelegate().getErrorMessage();
}
return null;
@@ -133,7 +143,7 @@ public class ContentAssistProcessorRegistry {
@Override
public IContextInformationValidator getContextInformationValidator() {
- if (isActive()) {
+ if (isActive(null)) {
return getDelegate().getContextInformationValidator();
}
return null;
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 6af35f9d1..61b3c0836 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
@@ -26,6 +26,7 @@ import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioningListener;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.contentassist.AsyncContentAssistant;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistant;
@@ -49,8 +50,8 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewe
private ITextEditor editor;
private Set<IContentType> contentTypes;
private IDocument document;
- private ContentAssistant contentAssistant;
- private IContentAssistProcessor contentAssistProcessor;
+ private AsyncContentAssistant contentAssistant;
+ private List<IContentAssistProcessor> processors;
/**
*
@@ -95,13 +96,15 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewe
@Override
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
ContentAssistProcessorRegistry registry= GenericEditorPlugin.getDefault().getContentAssistProcessorRegistry();
- contentAssistProcessor = new CompositeContentAssistProcessor(registry.getContentAssistProcessors(sourceViewer, getContentTypes()));
- contentAssistant = new ContentAssistant();
+ contentAssistant = new AsyncContentAssistant();
contentAssistant.setContextInformationPopupOrientation(ContentAssistant.CONTEXT_INFO_BELOW);
contentAssistant.setProposalPopupOrientation(ContentAssistant.PROPOSAL_REMOVE);
contentAssistant.enableColoredLabels(true);
contentAssistant.enableAutoActivation(true);
- contentAssistant.setContentAssistProcessor(contentAssistProcessor, IDocument.DEFAULT_CONTENT_TYPE);
+ this.processors = registry.getContentAssistProcessors(sourceViewer, getContentTypes());
+ for (IContentAssistProcessor processor : this.processors) {
+ contentAssistant.addContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE);
+ }
if (this.document != null) {
associateTokenContentTypes(this.document);
}
@@ -142,11 +145,13 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewe
}
private void associateTokenContentTypes(IDocument document) {
- if (contentAssistant == null) {
+ if (contentAssistant == null || this.processors == null) {
return;
}
for (String legalTokenContentType : document.getLegalContentTypes()) {
- contentAssistant.setContentAssistProcessor(this.contentAssistProcessor, legalTokenContentType);
+ for (IContentAssistProcessor processor : this.processors) {
+ contentAssistant.addContentAssistProcessor(processor, legalTokenContentType);
+ }
}
}

Back to the top