Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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