blob: 041723530326b741dfc61845662de9f830b6c5e4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 Rogue Wave Software 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:
* Michał Niewrzał (Rogue Wave Software Inc.) - initial implementation
*******************************************************************************/
package org.eclipse.lsp4e.test.completion;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.tests.util.DisplayHelper;
import org.eclipse.lsp4e.LanguageServersRegistry;
import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.ProjectSpecificLanguageServerWrapper;
import org.eclipse.lsp4e.operations.completion.LSContentAssistProcessor;
import org.eclipse.lsp4e.operations.completion.LSIncompleteCompletionProposal;
import org.eclipse.lsp4e.test.TestUtils;
import org.eclipse.lsp4e.tests.mock.MockLanguageSever;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class IncompleteCompletionTest {
private IProject project;
private LSContentAssistProcessor contentAssistProcessor;
@Before
public void setUp() throws CoreException {
project = TestUtils.createProject("CompletionTest" + System.currentTimeMillis());
contentAssistProcessor = new LSContentAssistProcessor();
}
@After
public void tearDown() throws CoreException {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().closeAllEditors(false);
project.delete(true, true, new NullProgressMonitor());
MockLanguageSever.INSTANCE.shutdown();
}
/*
* This tests the not-so-official way to associate a LS to a file programmatically, and then to retrieve the LS
* for the file independently of the content-types. Although doing it programatically isn't recommended, consuming
* file-specific LS already associated is something we want to support.
*/
@Test
public void testAssistForUnknownButConnectedType() throws CoreException, InvocationTargetException, IOException, InterruptedException {
List<CompletionItem> items = new ArrayList<>();
items.add(createCompletionItem("FirstClass", CompletionItemKind.Class));
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
IFile testFile = TestUtils.createUniqueTestFileOfUnknownType(project, "");
ITextViewer viewer = TestUtils.openTextViewer(testFile);
LanguageServerDefinition serverDefinition = LanguageServersRegistry.getInstance().getDefinition("org.eclipse.lsp4e.test.server");
assertNotNull(serverDefinition);
ProjectSpecificLanguageServerWrapper lsWrapperForConnection = LanguageServiceAccessor.getLSWrapperForConnection(testFile.getProject(), serverDefinition);
IPath fileLocation = testFile.getLocation();
// force connection (that's what LSP4E should be designed to prevent 3rd party from having to use it).
lsWrapperForConnection.connect(fileLocation, null);
new DisplayHelper() {
@Override
protected boolean condition() {
return lsWrapperForConnection.isConnectedTo(fileLocation);
}
}.waitForCondition(Display.getCurrent(), 3000);
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, 0);
assertEquals(items.size(), proposals.length);
// TODO compare both structures
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals(new Point("FirstClass".length(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testNoPrefix() throws CoreException, InvocationTargetException {
List<CompletionItem> items = new ArrayList<>();
items.add(createCompletionItem("FirstClass", CompletionItemKind.Class));
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
IFile testFile = TestUtils.createUniqueTestFile(project, "");
ITextViewer viewer = TestUtils.openTextViewer(testFile);
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, 0);
assertEquals(items.size(), proposals.length);
// TODO compare both structures
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals(new Point("FirstClass".length(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testPrefix() throws CoreException, InvocationTargetException {
List<CompletionItem> items = new ArrayList<>();
items.add(createCompletionItem("FirstClass", CompletionItemKind.Class));
items.add(createCompletionItem("SecondClass", CompletionItemKind.Class));
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
String content = "First";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project, content));
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, content.length());
assertEquals(2, proposals.length);
// TODO compare items
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals(new Point("FirstClass".length(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testPrefixCaseSensitivity() throws CoreException, InvocationTargetException {
List<CompletionItem> items = new ArrayList<>();
items.add(createCompletionItem("FirstClass", CompletionItemKind.Class));
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
String content = "FIRST";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project, content));
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, content.length());
assertEquals(1, proposals.length);
// TODO compare items
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals(new Point("FirstClass".length(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testCompleteOnFileEnd() throws CoreException, InvocationTargetException { // bug 508842
CompletionItem item = new CompletionItem();
item.setLabel("1024M");
item.setKind(CompletionItemKind.Value);
item.setTextEdit(new TextEdit(new Range(new Position(2, 10), new Position(2, 10)), "1024M"));
CompletionList completionList = new CompletionList(true, Collections.singletonList(item));
MockLanguageSever.INSTANCE.setCompletionList(completionList);
String content = "applications:\n" + "- name: hello\n" + " memory: ";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project, content));
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, content.length());
assertEquals(1, proposals.length);
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals(content + "1024M", viewer.getDocument().get());
assertEquals(new Point(viewer.getDocument().getLength(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testCompletionWithAdditionalEdits() throws CoreException, InvocationTargetException {
List<CompletionItem> items = new ArrayList<>();
CompletionItem item = new CompletionItem("additionaEditsCompletion");
item.setKind(CompletionItemKind.Function);
item.setInsertText("MainInsertText");
List<TextEdit> additionalTextEdits = new ArrayList<>();
TextEdit additionaEdit1 = new TextEdit(new Range(new Position(0, 6), new Position(0, 6)), "addOnText1");
TextEdit additionaEdit2 = new TextEdit(new Range(new Position(0, 12), new Position(0, 12)), "addOnText2");
additionalTextEdits.add(additionaEdit1);
additionalTextEdits.add(additionaEdit2);
item.setAdditionalTextEdits(additionalTextEdits);
items.add(item);
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
String content = "this <> is <> the main <> content of the file";
IFile testFile = TestUtils.createUniqueTestFile(project, content);
ITextViewer viewer = TestUtils.openTextViewer(testFile);
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, 24);
assertEquals(items.size(), proposals.length);
// TODO compare both structures
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
String newContent = viewer.getDocument().get();
assertEquals("this <addOnText1> is <addOnText2> the main <MainInsertText> content of the file", newContent);
}
@Test
public void testSnippetCompletionWithAdditionalEdits()
throws PartInitException, InvocationTargetException, CoreException {
CompletionItem item = new CompletionItem("snippet item");
item.setInsertText("$1 and ${2:foo}");
item.setKind(CompletionItemKind.Class);
item.setInsertTextFormat(InsertTextFormat.Snippet);
List<TextEdit> additionalTextEdits = new ArrayList<>();
TextEdit additionaEdit1 = new TextEdit(new Range(new Position(0, 6), new Position(0, 6)), "addOnText1");
TextEdit additionaEdit2 = new TextEdit(new Range(new Position(0, 12), new Position(0, 12)), "addOnText2");
additionalTextEdits.add(additionaEdit1);
additionalTextEdits.add(additionaEdit2);
item.setAdditionalTextEdits(additionalTextEdits);
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, Collections.singletonList(item)));
String content = "this <> is <> the main <> content of the file";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project, content));
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, 24);
assertEquals(1, proposals.length);
((LSIncompleteCompletionProposal) proposals[0]).apply(viewer.getDocument());
String newContent = viewer.getDocument().get();
assertEquals("this <addOnText1> is <addOnText2> the main < and foo> content of the file", newContent);
// TODO check link edit groups
}
@Test
public void testApplyCompletionWithPrefix() throws CoreException, InvocationTargetException {
Range range = new Range(new Position(0, 0), new Position(0, 5));
List<CompletionItem> items = Collections
.singletonList(createCompletionItem("FirstClass", CompletionItemKind.Class, range));
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
String content = "First";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project, content));
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, content.length());
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals(true, viewer.getDocument().get().equals("FirstClass"));
assertEquals(new Point(viewer.getDocument().getLength(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testApplyCompletionReplace() throws CoreException, InvocationTargetException {
Range range = new Range(new Position(0, 0), new Position(0, 20));
List<CompletionItem> items = Collections
.singletonList(createCompletionItem("FirstClass", CompletionItemKind.Class, range));
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
String content = "FirstNotMatchedLabel";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project, content));
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, 5);
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals("FirstClass", viewer.getDocument().get());
assertEquals(new Point("FirstClass".length(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testApplyCompletionReplaceAndTypingWithTextEdit() throws CoreException, InvocationTargetException, BadLocationException {
Range range = new Range(new Position(0, 0), new Position(0, 22));
List<CompletionItem> items = Collections
.singletonList(createCompletionItem("FirstClass", CompletionItemKind.Class, range));
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
String content = "FirstNotMatchedLabel";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project,content));
int invokeOffset = 5;
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, invokeOffset);
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
// simulate additional typing (to filter) after invoking completion
viewer.getDocument().replace(5, 0, "No");
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals("FirstClass", viewer.getDocument().get());
assertEquals(new Point("FirstClass".length(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testApplyCompletionReplaceAndTyping()
throws CoreException, InvocationTargetException, BadLocationException {
CompletionItem item = new CompletionItem("strncasecmp");
item.setKind(CompletionItemKind.Function);
item.setInsertText("strncasecmp()");
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, Collections.singletonList(item)));
String content = "str";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project, content));
int invokeOffset = content.length();
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, invokeOffset);
assertEquals(1, proposals.length);
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
// simulate additional typing (to filter) after invoking completion
viewer.getDocument().replace(content.length(), 0, "nc");
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals(item.getInsertText(), viewer.getDocument().get());
assertEquals(new Point(item.getInsertText().length(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
@Test
public void testCompletionReplace() throws CoreException, InvocationTargetException {
IFile file = TestUtils.createUniqueTestFile(project, "line1\nlineInsertHere");
ITextViewer viewer = TestUtils.openTextViewer(file);
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, Collections.singletonList(
createCompletionItem("Inserted", CompletionItemKind.Text, new Range(new Position(1, 4), new Position(1, 4 + "InsertHere".length())))
)));
int invokeOffset = viewer.getDocument().getLength() - "InsertHere".length();
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, invokeOffset);
LSIncompleteCompletionProposal LSIncompleteCompletionProposal = (LSIncompleteCompletionProposal) proposals[0];
LSIncompleteCompletionProposal.apply(viewer.getDocument());
assertEquals("line1\nlineInserted", viewer.getDocument().get());
assertEquals(new Point(viewer.getDocument().getLength(), 0),
LSIncompleteCompletionProposal.getSelection(viewer.getDocument()));
}
private CompletionItem createCompletionItem(String label, CompletionItemKind kind) {
return createCompletionItem(label, kind, new Range(new Position(0, 0), new Position(0, label.length())));
}
private CompletionItem createCompletionItem(String label, CompletionItemKind kind, Range range) {
CompletionItem item = new CompletionItem();
item.setLabel(label);
item.setKind(kind);
item.setTextEdit(new TextEdit(range, label));
return item;
}
@Test
public void testItemOrdering() throws Exception {
Range range = new Range(new Position(0, 0), new Position(0, 1));
List<CompletionItem> items = Arrays.asList(new CompletionItem[] {
createCompletionItem("AA", CompletionItemKind.Class, range),
createCompletionItem("AB", CompletionItemKind.Class, range),
createCompletionItem("BA", CompletionItemKind.Class, range),
createCompletionItem("BB", CompletionItemKind.Class, range),
createCompletionItem("CB", CompletionItemKind.Class, range),
createCompletionItem("CC", CompletionItemKind.Class, range),
});
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
String content = "B";
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project,content));
int invokeOffset = 1;
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, invokeOffset);
assertEquals(6, proposals.length);
assertEquals("AA", proposals[0].getDisplayString());
assertEquals("AB", proposals[1].getDisplayString());
assertEquals("BA", proposals[2].getDisplayString());
assertEquals("BB", proposals[3].getDisplayString());
((LSIncompleteCompletionProposal) proposals[0]).apply(viewer.getDocument());
assertEquals("AA", viewer.getDocument().get());
}
@Test
public void testBasicSnippet() throws PartInitException, InvocationTargetException, CoreException {
CompletionItem completionItem = createCompletionItem("$1 and ${2:foo}", CompletionItemKind.Class, new Range(new Position(0, 0), new Position(0, 1)));
completionItem.setInsertTextFormat(InsertTextFormat.Snippet);
MockLanguageSever.INSTANCE
.setCompletionList(new CompletionList(true, Collections.singletonList(completionItem)));
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project,""));
int invokeOffset = 0;
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, invokeOffset);
assertEquals(1, proposals.length);
((LSIncompleteCompletionProposal) proposals[0]).apply(viewer.getDocument());
assertEquals(" and foo", viewer.getDocument().get());
// TODO check link edit groups
}
@Test
public void testDuplicateVariable() throws PartInitException, InvocationTargetException, CoreException {
CompletionItem completionItem = createCompletionItem("${1:foo} and ${1:foo}", CompletionItemKind.Class, new Range(new Position(0, 0), new Position(0, 1)));
completionItem.setInsertTextFormat(InsertTextFormat.Snippet);
MockLanguageSever.INSTANCE
.setCompletionList(new CompletionList(true, Collections.singletonList(completionItem)));
ITextViewer viewer = TestUtils.openTextViewer(TestUtils.createUniqueTestFile(project,""));
int invokeOffset = 0;
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, invokeOffset);
assertEquals(1, proposals.length);
((LSIncompleteCompletionProposal) proposals[0]).apply(viewer.getDocument());
assertEquals("foo and foo", viewer.getDocument().get());
// TODO check link edit groups
}
@Test
public void testMultipleLS() throws Exception {
List<CompletionItem> items = new ArrayList<>();
items.add(createCompletionItem("FirstClass", CompletionItemKind.Class));
MockLanguageSever.INSTANCE.setCompletionList(new CompletionList(true, items));
IFile testFile = TestUtils.createUniqueTestFileMultiLS(project, "");
ITextViewer viewer = TestUtils.openTextViewer(testFile);
ICompletionProposal[] proposals = contentAssistProcessor.computeCompletionProposals(viewer, 0);
assertEquals(2 * items.size(), proposals.length);
}
}