Skip to content

Commit

Permalink
Implement "sourround with" style templates
Browse files Browse the repository at this point in the history
Adjusted SnippetContentAssistProcessor to create Range from editor text
selection (if present and non-empty) for TextEdit, so templates with
$TM_SELECTED_TEXT are able to sourround the selection.

Signed-off-by: Max Bureck <max.bureck@fokus.fraunhofer.de>
  • Loading branch information
Boereck committed Jan 7, 2020
1 parent e9ed90b commit f89f50a
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 23 deletions.
9 changes: 8 additions & 1 deletion org.eclipse.corrosion/snippets/rust.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,15 @@
"completionItemKind": 15,
"replacementLines": [
"thread::spawn(move || {",
" $0",
" ${0:$TM_SELECTED_TEXT}",
"});"
]
},
{
"display": "dbg!",
"completionItemKind": 15,
"replacementLines": [
"dbg!(${0:$TM_SELECTED_TEXT})"
]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@
import java.util.concurrent.ExecutionException;

import org.eclipse.corrosion.CorrosionPlugin;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
import org.eclipse.lsp4e.operations.completion.LSCompletionProposal;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.services.LanguageServer;
Expand All @@ -46,25 +45,17 @@ public Snippet(String display, CompletionItemKind kind, String[] replacementLine
}

public ICompletionProposal convertToCompletionProposal(int offset, LSPDocumentInfo info, String prefix,
String lineIndentation) {
String lineIndentation, Range textRange) {
CompletionItem item = new CompletionItem();
item.setLabel(display);
item.setKind(kind);
item.setInsertTextFormat(InsertTextFormat.Snippet);

Range r = null;
try {
int line = info.getDocument().getLineOfOffset(offset);
int lineOffset = offset - info.getDocument().getLineOffset(line);
r = new Range(new Position(line, lineOffset - prefix.length()), new Position(line, lineOffset));
} catch (BadLocationException e) {
// Caught by null return
}
if (r == null) {
return null;
}
item.setTextEdit(new TextEdit(r, createReplacement(lineIndentation)));
return new LSCompletionProposal(info.getDocument(), offset, item, getLanguageClient(info));
IDocument document = info.getDocument();
// if there is a text selection, take it, since snippets with $TM_SELECTED_TEXT
// will want to wrap the selection.
item.setTextEdit(new TextEdit(textRange, createReplacement(lineIndentation)));
return new LSCompletionProposal(document, offset, item, getLanguageClient(info));
}

private static LanguageServer getLanguageClient(LSPDocumentInfo info) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*
* Contributors:
* Lucas Bullen (Red Hat Inc.) - Initial implementation
* Max Bureck (Fraunhofer FOKUS) - Implemented "surround with" style snippets
*******************************************************************************/
package org.eclipse.corrosion.snippet;

Expand All @@ -20,21 +21,28 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.corrosion.CorrosionPlugin;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension9;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
Expand Down Expand Up @@ -98,16 +106,69 @@ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int
String indent = matcher.group("indent"); //$NON-NLS-1$
String prefix = matcher.group("prefix"); //$NON-NLS-1$

// Use range from selection (if available) to support "surround with" style
// completions
Range range = getRangeFromSelection(document, viewer).orElseGet(() -> {
// no selection available: get range from prefix
try {
int line = document.getLineOfOffset(offset);
int lineOffset = offset - document.getLineOffset(line);
Position start = new Position(line, lineOffset - prefix.length());
Position end = new Position(line, lineOffset);
return new Range(start, end);
} catch (BadLocationException e) {
return null;
}
});
if (range == null) {
return new ICompletionProposal[] {};
}

Collection<LSPDocumentInfo> infos = LanguageServiceAccessor.getLSPDocumentInfosFor(document,
capabilities -> Boolean.TRUE.equals(capabilities.getReferencesProvider()));
LSPDocumentInfo docInfo = infos.iterator().next();

List<ICompletionProposal> proposals = new ArrayList<>();
for (Snippet snippet : snippets) {
if (snippet.matchesPrefix(prefix)) {
proposals.add(snippet.convertToCompletionProposal(offset, infos.iterator().next(), prefix, indent));
}
ICompletionProposal[] proposals = snippets.stream().filter(s -> s.matchesPrefix(prefix))
.map(s -> s.convertToCompletionProposal(offset, docInfo, prefix, indent, range))
.toArray(ICompletionProposal[]::new);
return proposals;
}

/**
* Get the current selection from the given {@code viewer}. If there is a (non
* empty) selection returns a {@code Range} computed from the selection and
* returns this wrapped in an optional, otherwise returns an empty optional.
*
* @param document currently active document
* @param viewer the text viewer for the completion
* @return either an optional containing the text selection, or an empty
* optional, if there is no (non-empty) selection.
*/
private static Optional<Range> getRangeFromSelection(IDocument document, ITextViewer viewer) {
if (!(viewer instanceof ITextViewerExtension9)) {
return Optional.empty();
}
ITextViewerExtension9 textViewer = (ITextViewerExtension9) viewer;

ITextSelection textSelection = textViewer.getLastKnownSelection();
if (textSelection == null) {
return Optional.empty();
}
int selectionLength = textSelection.getLength();
if (selectionLength <= 0) {
return Optional.empty();
}

try {
int startOffset = textSelection.getOffset();
Position startPosition = LSPEclipseUtils.toPosition(startOffset, document);
int endOffset = startOffset + selectionLength;
Position endPosition = LSPEclipseUtils.toPosition(endOffset, document);

return Optional.of(new Range(startPosition, endPosition));
} catch (BadLocationException e) {
return Optional.empty();
}
return proposals.toArray(new ICompletionProposal[proposals.size()]);
}

private static boolean isOffsetInComment(String textToOffset) {
Expand Down

0 comments on commit f89f50a

Please sign in to comment.