Bug 558921 - NPE in async content assist with insert common prefix

Change-Id: Ibcfea620c5c7c5a8cdad2d00322403523cad7201
Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/AsyncContentAssistTest.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/AsyncContentAssistTest.java
index 0107265..5976c1b 100644
--- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/AsyncContentAssistTest.java
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/contentassist/AsyncContentAssistTest.java
@@ -15,13 +15,24 @@
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Widget;
 
 import org.eclipse.core.runtime.ILogListener;
 import org.eclipse.core.runtime.IStatus;
@@ -84,4 +95,49 @@
 		DisplayHelper.sleep(shell.getDisplay(), 1000);
 		assertNull(errorStatus);
 	}
+
+	@Test
+	public void testCompletePrefix() {
+		Shell shell = new Shell();
+		shell.setLayout(new FillLayout());
+		shell.setSize(500, 300);
+		SourceViewer viewer = new SourceViewer(shell, null, SWT.NONE);
+		Document document = new Document("b");
+		viewer.setDocument(document);
+		viewer.setSelectedRange(1, 0);
+		ContentAssistant contentAssistant = new ContentAssistant(true);
+		contentAssistant.addContentAssistProcessor(new BarContentAssistProcessor(), IDocument.DEFAULT_CONTENT_TYPE);
+		contentAssistant.enablePrefixCompletion(true);
+		contentAssistant.install(viewer);
+		shell.open();
+		Display display = shell.getDisplay();
+		final Set<Shell> beforeShells = Arrays.stream(display.getShells()).filter(Shell::isVisible).collect(Collectors.toSet());
+		contentAssistant.showPossibleCompletions();
+		assertTrue("Completion item not shown", new DisplayHelper() {
+			@Override
+			protected boolean condition() {
+				Set<Shell> newShells = Arrays.stream(display.getShells()).filter(Shell::isVisible).collect(Collectors.toSet());
+				newShells.removeAll(beforeShells);
+				if (!newShells.isEmpty()) {
+					Table completionTable = findCompletionSelectionControl(newShells.iterator().next());
+					return Arrays.stream(completionTable.getItems()).map(TableItem::getText).anyMatch(item -> item.contains(BarContentAssistProcessor.PROPOSAL.substring(document.getLength())));
+				}
+				return false;
+			}
+		}.waitForCondition(display, 2000));
+	}
+
+	private static Table findCompletionSelectionControl(Widget control) {
+		if (control instanceof Table) {
+			return (Table)control;
+		} else if (control instanceof Composite) {
+			for (Widget child : ((Composite)control).getChildren()) {
+				Table res = findCompletionSelectionControl(child);
+				if (res != null) {
+					return res;
+				}
+			}
+		}
+		return null;
+	}
 }
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
index e39bc2f..3395dc5 100644
--- 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
@@ -286,15 +286,15 @@
 
 		computeAndPopulateProposals(fInvocationOffset, (List<ICompletionProposal> proposals) -> {
 			ensureDocumentListenerInstalled();
+			fFilteredProposals= proposals;
 			if (!proposals.isEmpty() && completeCommonPrefix()) {
 				hide();
 			} else {
-				fFilteredProposals= proposals;
 				setProposals(proposals, false);
 				displayProposals();
 			}
 		}, true, false, true);
-		fFilteredProposals= new ArrayList<>(fComputedProposals);
+		fFilteredProposals= new ArrayList<>(fComputedProposals != null ? fComputedProposals : Collections.emptyList());
 		return getErrorMessage();
 	}