Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix NPE if there is no completion provider #999

Merged
merged 1 commit into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private LSPEclipseUtils() {
// this class shouldn't be instantiated
}

public static Position toPosition(int offset, IDocument document) throws BadLocationException {
public static @NonNull Position toPosition(int offset, IDocument document) throws BadLocationException {
final var res = new Position();
res.setLine(document.getLineOfOffset(offset));
res.setCharacter(offset - document.getLineInformationOfOffset(offset).getOffset());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemDefaults;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.ExecuteCommandOptions;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.InsertReplaceEdit;
Expand Down Expand Up @@ -152,10 +153,11 @@ public LSCompletionProposal(@NonNull IDocument document, int offset, @NonNull Co
if (item.getInsertTextMode() == null) {
item.setInsertTextMode(defaults.getInsertTextMode());
}
if (item.getTextEditText() != null && defaults.getEditRange() != null) {
String textEditText = item.getTextEditText();
if (textEditText != null && defaults.getEditRange() != null) {
item.setTextEdit(defaults.getEditRange().map(
range -> Either.forLeft(new TextEdit(range, item.getTextEditText())),
insertReplaceRange -> Either.forRight(new InsertReplaceEdit(item.getTextEditText(), insertReplaceRange.getInsert(), insertReplaceRange.getReplace()))));
range -> Either.forLeft(new TextEdit(range, textEditText)),
insertReplaceRange -> Either.forRight(new InsertReplaceEdit(textEditText, insertReplaceRange.getInsert(), insertReplaceRange.getReplace()))));
}
}
}
Expand All @@ -172,7 +174,7 @@ public String getDocumentFilter(int offset) throws BadLocationException {
currentOffset = offset;
rankScore = null;
rankCategory = null;
documentFilterAddition = offset > this.initialOffset ? document.get(initialOffset, offset - initialOffset) : ""; //$NON-NLS-1$
documentFilterAddition = offset > initialOffset ? document.get(initialOffset, offset - initialOffset) : ""; //$NON-NLS-1$
}
return documentFilter + documentFilterAddition;
}
Expand Down Expand Up @@ -237,15 +239,15 @@ public int getRankCategory() {
}

public int getBestOffset() {
return this.bestOffset;
return bestOffset;
}

public void updateOffset(int offset) {
this.bestOffset = getPrefixCompletionStart(document, offset);
bestOffset = getPrefixCompletionStart(document, offset);
}

public CompletionItem getItem() {
return this.item;
return item;
}

private boolean isDeprecated() {
Expand All @@ -258,7 +260,7 @@ public StyledString getStyledDisplayString(IDocument document, int offset, BoldS
StyledString res = isDeprecated()
? new StyledString(rawString, StyleUtil.DEPRECATE)
: new StyledString(rawString);
if (offset > this.bestOffset) {
if (offset > bestOffset) {
try {
String subString = getDocumentFilter(offset).toLowerCase();
int lastIndex = 0;
Expand Down Expand Up @@ -291,7 +293,7 @@ public void applyStyles(TextStyle textStyle) {

@Override
public String getDisplayString() {
return this.item.getLabel();
return item.getLabel();
}

@Override
Expand Down Expand Up @@ -324,23 +326,16 @@ protected IInformationControl doCreateInformationControl(Shell parent) {

@Override
public String getAdditionalProposalInfo(IProgressMonitor monitor) {
if (languageServerWrapper.isActive() && languageServerWrapper.getServerCapabilities().getCompletionProvider().getResolveProvider()) {
try {
languageServerWrapper.execute(ls -> ls.getTextDocumentService().resolveCompletionItem(item).thenAccept(this::updateCompletionItem))
.get(RESOLVE_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
LanguageServerPlugin.logError(e);
} catch (InterruptedException e) {
LanguageServerPlugin.logError(e);
Thread.currentThread().interrupt();
} catch (TimeoutException e) {
LanguageServerPlugin.logWarning("Could not resolve completion items due to timeout after " + RESOLVE_TIMEOUT + " milliseconds in `completionItem/resolve`", e); //$NON-NLS-1$//$NON-NLS-2$
if (languageServerWrapper.isActive()) {
CompletionOptions completionProvider = languageServerWrapper.getServerCapabilities().getCompletionProvider();
if (completionProvider != null && completionProvider.getResolveProvider()) {
resolveItem();
}
}

final var res = new StringBuilder();
if (this.item.getDetail() != null && !this.item.getDetail().isEmpty()) {
res.append("<p>" + this.item.getDetail() + "</p>"); //$NON-NLS-1$ //$NON-NLS-2$
if (item.getDetail() != null && !item.getDetail().isEmpty()) {
res.append("<p>" + item.getDetail() + "</p>"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (res.length() > 0) {
res.append("<br/>"); //$NON-NLS-1$
Expand All @@ -355,6 +350,20 @@ public String getAdditionalProposalInfo(IProgressMonitor monitor) {
return res.toString();
}

private void resolveItem() {
try {
languageServerWrapper.execute(ls -> ls.getTextDocumentService().resolveCompletionItem(item).thenAccept(this::updateCompletionItem))
.get(RESOLVE_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
LanguageServerPlugin.logError(e);
} catch (InterruptedException e) {
LanguageServerPlugin.logError(e);
Thread.currentThread().interrupt();
} catch (TimeoutException e) {
LanguageServerPlugin.logWarning("Could not resolve completion items due to timeout after " + RESOLVE_TIMEOUT + " milliseconds in `completionItem/resolve`", e); //$NON-NLS-1$//$NON-NLS-2$
}
}

private void updateCompletionItem(CompletionItem resolvedItem) {
if (resolvedItem == null) {
return;
Expand Down Expand Up @@ -387,12 +396,12 @@ private void updateCompletionItem(CompletionItem resolvedItem) {

@Override
public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
return item.getInsertText().substring(completionOffset - this.bestOffset);
return item.getInsertText().substring(completionOffset - bestOffset);
}

@Override
public int getPrefixCompletionStart(IDocument document, int completionOffset) {
Either<TextEdit, InsertReplaceEdit> textEdit = this.item.getTextEdit();
Either<TextEdit, InsertReplaceEdit> textEdit = item.getTextEdit();
if (textEdit != null) {
try {
return LSPEclipseUtils.toOffset(getTextEditRange().getStart(), document);
Expand Down Expand Up @@ -433,12 +442,12 @@ protected void apply(IDocument document, char trigger, int stateMask, int offset
try {
if (textEdit == null) {
insertText = getInsertText();
Position start = LSPEclipseUtils.toPosition(this.bestOffset, document);
Position start = LSPEclipseUtils.toPosition(bestOffset, document);
Position end = LSPEclipseUtils.toPosition(offset, document); // need 2 distinct objects
textEdit = new TextEdit(new Range(start, end), insertText);
} else if (offset > this.initialOffset) {
} else if (offset > initialOffset) {
// characters were added after completion was activated
int shift = offset - this.initialOffset;
int shift = offset - initialOffset;
textEdit.getRange().getEnd().setCharacter(textEdit.getRange().getEnd().getCharacter() + shift);
}
{ // workaround https://github.com/Microsoft/vscode/issues/17036
Expand All @@ -460,11 +469,11 @@ protected void apply(IDocument document, char trigger, int stateMask, int offset

if (insertText != null) {
// try to reuse existing characters after completion location
int shift = offset - this.bestOffset;
int shift = offset - bestOffset;
int commonSize = 0;
while (commonSize < insertText.length() - shift
&& document.getLength() > offset + commonSize
&& document.getChar(this.bestOffset + shift + commonSize) == insertText.charAt(commonSize + shift)) {
&& document.getChar(bestOffset + shift + commonSize) == insertText.charAt(commonSize + shift)) {
commonSize++;
}
textEdit.getRange().getEnd().setCharacter(textEdit.getRange().getEnd().getCharacter() + commonSize);
Expand Down Expand Up @@ -492,12 +501,12 @@ protected void apply(IDocument document, char trigger, int stateMask, int offset
final var allEdits = new ArrayList<TextEdit>();
allEdits.add(textEdit);
additionalEdits.stream().forEach(te -> {
int shift = offset - this.initialOffset;
int shift = offset - initialOffset;
if (shift != 0) {
try {
int start = LSPEclipseUtils.toOffset(te.getRange().getStart(), document);
int end = LSPEclipseUtils.toOffset(te.getRange().getEnd(), document);
if (start > this.initialOffset && te.getRange().getStart().getLine() == initialPosition.getLine()) {
if (start > initialOffset && te.getRange().getStart().getLine() == initialPosition.getLine()) {
// We need to shift the Range according to the shift (if on the same line)
te.getRange().setStart(LSPEclipseUtils.toPosition(start + shift, document));
te.getRange().setEnd(LSPEclipseUtils.toPosition(end + shift, document));
Expand Down Expand Up @@ -662,7 +671,7 @@ private Range getTextEditRange() throws BadLocationException {
if (textEdit != null) {
return textEdit.map(TextEdit::getRange, InsertReplaceEdit::getInsert);
} else {
Position start = LSPEclipseUtils.toPosition(this.bestOffset, document);
Position start = LSPEclipseUtils.toPosition(bestOffset, document);
Position end = LSPEclipseUtils.toPosition(initialOffset, document);
return new Range(start, end);
}
Expand All @@ -688,21 +697,21 @@ private String getAbsoluteLocation(IPath path) {
}

protected String getInsertText() {
String insertText = this.item.getInsertText();
Either<TextEdit, InsertReplaceEdit> eitherTextEdit = this.item.getTextEdit();
String insertText = item.getInsertText();
Either<TextEdit, InsertReplaceEdit> eitherTextEdit = item.getTextEdit();
if (eitherTextEdit != null) {
insertText = eitherTextEdit.map(TextEdit::getNewText, InsertReplaceEdit::getNewText);
}
if (insertText == null) {
insertText = this.item.getLabel();
insertText = item.getLabel();
}
return insertText;
}

@Override
public Point getSelection(IDocument document) {
if (this.firstPosition != null) {
return new Point(this.firstPosition.getOffset(), this.firstPosition.getLength());
if (firstPosition != null) {
return new Point(firstPosition.getOffset(), firstPosition.getLength());
}
if (selection == null) {
return null;
Expand All @@ -712,12 +721,12 @@ public Point getSelection(IDocument document) {

@Override
public String getAdditionalProposalInfo() {
return this.getAdditionalProposalInfo(new NullProgressMonitor());
return getAdditionalProposalInfo(new NullProgressMonitor());
}

@Override
public Image getImage() {
return LSPImages.imageFromCompletionItem(this.item);
return LSPImages.imageFromCompletionItem(item);
}

@Override
Expand Down Expand Up @@ -751,7 +760,7 @@ public String getFilterString() {

@Override
public boolean isValidFor(IDocument document, int offset) {
return (!isIncomplete || offset == this.initialOffset) && validate(document, offset, null);
return (!isIncomplete || offset == initialOffset) && validate(document, offset, null);
}

@Override
Expand All @@ -768,7 +777,7 @@ public boolean validate(IDocument document, int offset, DocumentEvent event) {
if (item.getLabel() == null || item.getLabel().isEmpty()) {
return false;
}
if (offset < this.bestOffset) {
if (offset < bestOffset) {
return false;
}
try {
Expand Down Expand Up @@ -797,12 +806,11 @@ public void apply(IDocument document, char trigger, int offset) {

@Override
public void apply(IDocument document) {
apply(document, Character.MIN_VALUE, 0, this.bestOffset);
apply(document, Character.MIN_VALUE, 0, bestOffset);
}

@Override
public char[] getTriggerCharacters() {
// TODO Auto-generated method stub
return null;
}

Expand Down