Add support to include imports through additional edits

This commit is contained in:
nsingh
2017-09-20 16:08:05 -07:00
parent 5fe87f6522
commit 00c3213adc
7 changed files with 168 additions and 29 deletions

View File

@@ -15,11 +15,9 @@ import java.util.Optional;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.boot.java.handlers.SimpleCompletionFactory;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import com.google.common.base.Supplier;
@@ -46,15 +44,14 @@ public class JavaSnippet {
}
public Optional<ICompletionProposal> generateCompletion(Supplier<SnippetBuilder> snippetBuilderFactory,
IDocument doc, int offset, ASTNode node, String query) {
DocumentRegion query, ASTNode node) {
if (context.appliesTo(node)) {
return Optional.of(
SimpleCompletionFactory.simpleProposal(doc,
offset,
new JavaSnippetCompletion(snippetBuilderFactory,
query,
kind, template, "Snippet", Renderables.NO_DESCRIPTION
).setLabel(name)
this
)
);
}
@@ -65,4 +62,16 @@ public class JavaSnippet {
return this.name;
}
public String getTemplate() {
return this.template;
}
public List<String> getImports() {
return this.imports;
}
public CompletionItemKind getKind() {
return kind;
}
}

View File

@@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.snippets;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
import com.google.common.base.Supplier;
public class JavaSnippetBuilder{
private Supplier<SnippetBuilder> snippetBuilderFactory;
public JavaSnippetBuilder(Supplier<SnippetBuilder> snippetBuilderFactory) {
this.snippetBuilderFactory = snippetBuilderFactory;
}
public DocumentEdits createEdit(DocumentRegion query, String template) {
DocumentEdits edit = new DocumentEdits(query.getDocument());
edit.replace(query.getStart(), query.getEnd(), template);
return edit;
}
}

View File

@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.snippets;
import java.util.Optional;
import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import com.google.common.base.Supplier;
public class JavaSnippetCompletion implements ICompletionProposal{
private DocumentRegion query;
private JavaSnippet javaSnippet;
private Supplier<SnippetBuilder> snippetBuilderFactory;
public JavaSnippetCompletion(Supplier<SnippetBuilder> snippetBuilderFactory, DocumentRegion query, JavaSnippet javaSnippet) {
this.snippetBuilderFactory = snippetBuilderFactory;
this.query = query;
this.javaSnippet = javaSnippet;
}
@Override
public String getLabel() {
return javaSnippet.getName();
}
@Override
public CompletionItemKind getKind() {
return javaSnippet.getKind();
}
@Override
public DocumentEdits getTextEdit() {
return new JavaSnippetBuilder(snippetBuilderFactory).createEdit(query, javaSnippet.getTemplate());
}
@Override
public String getDetail() {
return "Snippet";
}
@Override
public Renderable getDocumentation() {
return Renderables.NO_DESCRIPTION;
}
@Override
public Optional<DocumentEdits> getAdditionalEdit() {
DocumentEdits edit = new DocumentEdits(query.getDocument());
edit.insert(0, "import my.stuff;\n");
return Optional.of(edit );
}
}

View File

@@ -16,6 +16,7 @@ import java.util.List;
import org.eclipse.jdt.core.dom.ASTNode;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.languageserver.util.PrefixFinder;
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
@@ -48,11 +49,11 @@ public class JavaSnippetManager {
public Collection<ICompletionProposal> getCompletions(IDocument doc, int offset, ASTNode node) {
Collection<ICompletionProposal> completions = new ArrayList<>();
String query = PREFIX_FINDER.getPrefix(doc, offset);
DocumentRegion query = PREFIX_FINDER.getPrefixRegion(doc, offset);
for (JavaSnippet javaSnippet : snippets) {
if (FuzzyMatcher.matchScore(query, javaSnippet.getName()) != 0) {
javaSnippet.generateCompletion(snippetBuilderFactory, doc, offset, node, query)
if (FuzzyMatcher.matchScore(query.toString(), javaSnippet.getName()) != 0) {
javaSnippet.generateCompletion(snippetBuilderFactory, query, node)
.ifPresent((completion) -> completions.add(completion));
}
}

View File

@@ -11,6 +11,8 @@
package org.springframework.ide.vscode.commons.languageserver.completion;
import java.util.Optional;
import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.commons.util.Renderable;
@@ -23,6 +25,7 @@ public interface ICompletionProposal {
String getLabel();
CompletionItemKind getKind();
DocumentEdits getTextEdit();
default Optional<DocumentEdits> getAdditionalEdit() { return Optional.empty(); }
String getDetail();
Renderable getDocumentation();

View File

@@ -15,6 +15,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@@ -35,6 +36,8 @@ import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
@@ -174,7 +177,19 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
private static void resolveItem(TextDocument doc, ICompletionProposal completion, CompletionItem item) throws Exception {
item.setDocumentation(toMarkdown(completion.getDocumentation()));
adaptEdits(item, doc, completion.getTextEdit());
Optional<TextEdit> mainEdit = adaptEdits(doc, completion.getTextEdit());
if (mainEdit.isPresent()) {
item.setTextEdit(mainEdit.get());
item.setInsertTextFormat(InsertTextFormat.Snippet);
} else {
item.setInsertText("");
}
completion.getAdditionalEdit().ifPresent(edit -> {
adaptEdits(doc, edit).ifPresent(extraEdit -> {
item.setAdditionalTextEdits(ImmutableList.of(extraEdit));
});
});
}
private static String toMarkdown(Renderable r) {
@@ -184,24 +199,28 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
return null;
}
private static void adaptEdits(CompletionItem item, TextDocument doc, DocumentEdits edits) throws Exception {
TextReplace replaceEdit = edits.asReplacement(doc);
if (replaceEdit==null) {
//The original edit does nothing.
item.setInsertText("");
} else {
TextDocument newDoc = doc.copy();
edits.apply(newDoc);
TextEdit vscodeEdit = new TextEdit();
vscodeEdit.setRange(doc.toRange(replaceEdit.start, replaceEdit.end-replaceEdit.start));
if (Boolean.getBoolean("lsp.completions.indentation.enable")) {
vscodeEdit.setNewText(replaceEdit.newText);
private static Optional<TextEdit> adaptEdits(TextDocument doc, DocumentEdits edits) {
try {
TextReplace replaceEdit = edits.asReplacement(doc);
if (replaceEdit==null) {
//The original edit does nothing.
return Optional.empty();
} else {
vscodeEdit.setNewText(vscodeIndentFix(doc, vscodeEdit.getRange().getStart(), replaceEdit.newText));
TextDocument newDoc = doc.copy();
edits.apply(newDoc);
TextEdit vscodeEdit = new TextEdit();
vscodeEdit.setRange(doc.toRange(replaceEdit.start, replaceEdit.end-replaceEdit.start));
if (Boolean.getBoolean("lsp.completions.indentation.enable")) {
vscodeEdit.setNewText(replaceEdit.newText);
} else {
vscodeEdit.setNewText(vscodeIndentFix(doc, vscodeEdit.getRange().getStart(), replaceEdit.newText));
}
//TODO: cursor offset within newText? for now we assume its always at the end.
return Optional.of(vscodeEdit);
}
//TODO: cursor offset within newText? for now we assume its always at the end.
item.setTextEdit(vscodeEdit);
item.setInsertTextFormat(InsertTextFormat.Snippet);
} catch (Exception e) {
Log.log(e);
return Optional.empty();
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* Copyright (c) 2015, 2017 Pivotal, Inc.
* 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
@@ -32,4 +32,9 @@ public abstract class PrefixFinder {
return getPrefix(doc, offset, 0);
}
protected abstract boolean isPrefixChar(char c);
public DocumentRegion getPrefixRegion(IDocument doc, int offset) {
String prefix = getPrefix(doc, offset);
return prefix != null ? new DocumentRegion(doc, offset - prefix.length(), offset) : null;
}
}