Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulian Honnen2020-01-21 15:51:17 +0000
committerMickael Istria2020-01-22 15:29:24 +0000
commitb56064d7e37949396298deb9058b32457d12a733 (patch)
tree91760428b9597d751edcab538b8620661678775e
parente78727f0dc0b653b9d5b37cfd6f145f6dfc1a4fc (diff)
downloadeclipse.platform.text-b56064d7e37949396298deb9058b32457d12a733.tar.gz
eclipse.platform.text-b56064d7e37949396298deb9058b32457d12a733.tar.xz
eclipse.platform.text-b56064d7e37949396298deb9058b32457d12a733.zip
Bug 559251 - restored missing validate() events during async computationI20200123-0525I20200123-0430I20200122-1805
Only clear the occurred document events when they were actually consumed by the filtering. While the async computation is running, filtering is postponed and events are left in the queue. This ensures that ICompletionProposalExtension2 can adapt their replacement length correctly. Replaced fIsFilterPending with an AtomicBoolean to avoid synchronization issues when accessed by computation thread. Change-Id: I92ec82eb43a52301aa0116f00e5d703840348187 Signed-off-by: Julian Honnen <julian.honnen@vector.com>
-rw-r--r--org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/FilteringAsyncContentAssistTests.java68
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java14
2 files changed, 72 insertions, 10 deletions
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/FilteringAsyncContentAssistTests.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/FilteringAsyncContentAssistTests.java
index 73f9826dfac..836cfd373d2 100644
--- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/FilteringAsyncContentAssistTests.java
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/FilteringAsyncContentAssistTests.java
@@ -15,6 +15,7 @@ import static org.junit.Assert.assertTrue;
import java.lang.reflect.Field;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -153,7 +154,7 @@ public class FilteringAsyncContentAssistTests {
((ICompletionProposalExtension) filteredProposals.get(0)).apply(document, (char) 0,
viewer.getSelectedRange().x);
- assertEquals("xxx", document.get());
+ assertEquals("xx", document.get());
}
/**
@@ -312,6 +313,46 @@ public class FilteringAsyncContentAssistTests {
filteredProposals = getFilteredProposals(ca, p -> p instanceof IncompleteCompletionProposal);
assertTrue(filteredProposals == null || filteredProposals.isEmpty());
}
+
+ @Test
+ public void testProposalValidation() throws Exception {
+ IDocument document= viewer.getDocument();
+
+ BlockingProcessor processor= new BlockingProcessor("abcd()");
+ ca.addContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE);
+
+ ca.install(viewer);
+
+ viewer.setSelectedRange(0, 0);
+
+ ca.showPossibleCompletions();
+ DisplayHelper.sleep(shell.getDisplay(), 50);
+
+ new InsertEdit(0, "a").apply(document);
+ viewer.setSelectedRange(1, 0);
+ new InsertEdit(1, "b").apply(document);
+ viewer.setSelectedRange(2, 0);
+
+ processor.blocked.countDown();
+ DisplayHelper.sleep(shell.getDisplay(), 100);
+
+ new InsertEdit(2, "c").apply(document);
+ viewer.setSelectedRange(3, 0);
+ new InsertEdit(3, "d").apply(document);
+ viewer.setSelectedRange(4, 0);
+
+ DisplayHelper.sleep(shell.getDisplay(), 100);
+
+ List<ICompletionProposal> filteredProposals= getFilteredProposals(ca,
+ p -> p instanceof CompletionProposal);
+ assertTrue(filteredProposals != null);
+ assertEquals(1, filteredProposals.size());
+
+ filteredProposals.get(0).apply(document);
+
+ assertEquals("abcd()", document.get());
+
+ }
private class ImmediateContentAssistProcessor implements IContentAssistProcessor {
@@ -408,6 +449,26 @@ public class FilteringAsyncContentAssistTests {
return completionProposals;
}
}
+
+ private class BlockingProcessor extends ImmediateContentAssistProcessor {
+
+ final CountDownLatch blocked= new CountDownLatch(1);
+
+ BlockingProcessor(String template) {
+ super(template, false);
+ }
+
+ @Override
+ public ICompletionProposal[] computeCompletionProposals(ITextViewer textViewer, int offset) {
+ try {
+ blocked.await();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("Cannot generate delayed content assist proposals!");
+ }
+
+ return super.computeCompletionProposals(textViewer, offset);
+ }
+ }
@SuppressWarnings("unchecked")
private static List<ICompletionProposal> getComputedProposals(ContentAssistant ca) throws Exception {
@@ -456,7 +517,7 @@ public class FilteringAsyncContentAssistTests {
/** The replacement offset. */
protected int fReplacementOffset;
/** The replacement length. */
- private int fReplacementLength;
+ protected int fReplacementLength;
/** The cursor position after this proposal has been applied. */
private int fCursorPosition;
@@ -530,6 +591,9 @@ public class FilteringAsyncContentAssistTests {
@Override
public boolean validate(IDocument document, int offset, DocumentEvent event) {
+ if (event != null) {
+ fReplacementLength += event.fText.length() - event.fLength;
+ }
if (offset > fReplacementOffset) {
try {
return isSubstringFoundOrderedInString(document.get(fReplacementOffset, offset - fReplacementOffset), fReplacementString);
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 32f74603ed5..1ed5f4f7235 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
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.osgi.util.TextProcessor;
@@ -350,11 +351,9 @@ class CompletionProposalPopup implements IContentAssistListener {
private final Runnable fFilterRunnable= new Runnable() {
@Override
public void run() {
- if (!fIsFilterPending)
+ if (!fIsFilterPending.compareAndSet(true, false))
return;
- fIsFilterPending= false;
-
if (!Helper.okToUse(fContentAssistSubjectControlAdapter.getControl()))
return;
@@ -367,7 +366,6 @@ class CompletionProposalPopup implements IContentAssistListener {
proposals= computeFilteredProposals(offset, event);
}
} catch (BadLocationException x) {
- } finally {
fDocumentEvents.clear();
}
fFilterOffset= offset;
@@ -395,7 +393,7 @@ class CompletionProposalPopup implements IContentAssistListener {
*
* @since 3.1.1
*/
- private boolean fIsFilterPending= false;
+ private final AtomicBoolean fIsFilterPending= new AtomicBoolean(false);
/**
* The info message at the bottom of the popup, or <code>null</code> for no popup (if
* ContentAssistant does not provide one).
@@ -936,7 +934,7 @@ class CompletionProposalPopup implements IContentAssistListener {
/* Make sure that there is no filter runnable pending.
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=31427
*/
- if (fIsFilterPending)
+ if (fIsFilterPending.get())
fFilterRunnable.run();
// filter runnable may have hidden the proposals
@@ -1494,8 +1492,7 @@ class CompletionProposalPopup implements IContentAssistListener {
* offset of the original invocation of the content assistant.
*/
void filterProposals() {
- if (!fIsFilterPending) {
- fIsFilterPending= true;
+ if (fIsFilterPending.compareAndSet(false, true)) {
Control control= fContentAssistSubjectControlAdapter.getControl();
control.getDisplay().asyncExec(fFilterRunnable);
}
@@ -1511,6 +1508,7 @@ class CompletionProposalPopup implements IContentAssistListener {
* @since 3.0
*/
List<ICompletionProposal> computeFilteredProposals(int offset, DocumentEvent event) {
+ fDocumentEvents.clear();
if (offset == fInvocationOffset && event == null) {
fIsFilteredSubset= false;

Back to the top