diff options
author | Julian Honnen | 2020-01-21 15:51:17 +0000 |
---|---|---|
committer | Mickael Istria | 2020-01-22 15:29:24 +0000 |
commit | b56064d7e37949396298deb9058b32457d12a733 (patch) | |
tree | 91760428b9597d751edcab538b8620661678775e | |
parent | e78727f0dc0b653b9d5b37cfd6f145f6dfc1a4fc (diff) | |
download | eclipse.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>
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; |