Skip to content

Commit

Permalink
fix: HTML completion / autoclose is broken when offset is inside a Qute
Browse files Browse the repository at this point in the history
section (#787)

Fixes #787

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jul 10, 2023
1 parent 0f081b9 commit 1a9c902
Show file tree
Hide file tree
Showing 48 changed files with 2,026 additions and 258 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ public class SupportedFeatures {
textDocumentClientCapabilities.setInlayHint(new InlayHintCapabilities());
// TODO : support textDocument/colorPresentation
// textDocumentClientCapabilities.setColorProvider(new ColorProviderCapabilities());
final var completionItemCapabilities = new CompletionItemCapabilities(Boolean.TRUE);
final var completionItemCapabilities = new CompletionItemCapabilities(Boolean.FALSE);
completionItemCapabilities
.setDocumentationFormat(Arrays.asList(MarkupKind.MARKDOWN, MarkupKind.PLAINTEXT));
completionItemCapabilities.setInsertTextModeSupport(new CompletionItemInsertTextModeSupportCapabilities(List.of(InsertTextMode.AsIs, InsertTextMode.AdjustIndentation)));
completionItemCapabilities.setResolveSupport(new CompletionItemResolveSupportCapabilities(List.of("documentation", "detail", "additionalTextEdits"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
// completionItemCapabilities.setInsertTextModeSupport(new CompletionItemInsertTextModeSupportCapabilities(List.of(InsertTextMode.AsIs, InsertTextMode.AdjustIndentation)));
// completionItemCapabilities.setResolveSupport(new CompletionItemResolveSupportCapabilities(List.of("documentation", "detail", "additionalTextEdits"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
textDocumentClientCapabilities.setCompletion(new CompletionCapabilities(completionItemCapabilities));
final var definitionCapabilities = new DefinitionCapabilities();
definitionCapabilities.setLinkSupport(Boolean.TRUE);
Expand Down Expand Up @@ -115,11 +115,13 @@ public class SupportedFeatures {
textDocumentClientCapabilities.setOnTypeFormatting(null); // TODO
// TODO : support textDocument/rangeFormatting
// textDocumentClientCapabilities.setRangeFormatting(new RangeFormattingCapabilities());
textDocumentClientCapabilities.setReferences(new ReferencesCapabilities());
final var renameCapabilities = new RenameCapabilities();
renameCapabilities.setPrepareSupport(true);
textDocumentClientCapabilities.setRename(renameCapabilities);
// TODO
// TODO : support textDocument/references
// textDocumentClientCapabilities.setReferences(new ReferencesCapabilities());
// TODO : support textDocument/rename
//final var renameCapabilities = new RenameCapabilities();
//renameCapabilities.setPrepareSupport(true);
//textDocumentClientCapabilities.setRename(renameCapabilities);
// TODO : support textDocument/signatureHelp
// textDocumentClientCapabilities.setSignatureHelp(new SignatureHelpCapabilities());
textDocumentClientCapabilities
.setSynchronization(new SynchronizationCapabilities(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE));
Expand Down Expand Up @@ -151,9 +153,9 @@ public class SupportedFeatures {

public static WindowClientCapabilities getWindowClientCapabilities() {
final var windowClientCapabilities = new WindowClientCapabilities();
windowClientCapabilities.setShowDocument(new ShowDocumentCapabilities(true));
windowClientCapabilities.setWorkDoneProgress(true);
windowClientCapabilities.setShowMessage(new WindowShowMessageRequestCapabilities());
//windowClientCapabilities.setShowDocument(new ShowDocumentCapabilities(true));
//windowClientCapabilities.setWorkDoneProgress(true);
//windowClientCapabilities.setShowMessage(new WindowShowMessageRequestCapabilities());
return windowClientCapabilities;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public int getPrefixCompletionStart(Document document, int completionOffset) {
@NotNull
@Override
public String getLookupString() {
String lookup = StringUtils.isNotBlank(item.getSortText())?item.getSortText():item.getLabel();
String lookup = StringUtils.isNotBlank(item.getFilterText())?item.getFilterText():item.getLabel();
if (lookup.charAt(0) == '@') {
return lookup.substring(1);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.redhat.devtools.intellij.lsp4ij.operations.completion;

import com.intellij.codeInsight.completion.CompletionConfidence;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.ThreeState;
import org.jetbrains.annotations.NotNull;

public class LSPCompletionConfidence extends CompletionConfidence {

@Override
public @NotNull ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) {
return ThreeState.NO;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.redhat.devtools.intellij.lsp4ij.operations.highlight;

import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.PsiElementBase;
import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils;
import org.eclipse.lsp4j.DocumentHighlight;
import org.eclipse.lsp4j.DocumentHighlightKind;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class LSPHighlightPsiElement extends PsiElementBase {

private final TextRange textRange;
private final DocumentHighlightKind kind;

public LSPHighlightPsiElement(TextRange textRange, DocumentHighlightKind kind) {
this.textRange = textRange;
this.kind = kind;
}

public DocumentHighlightKind getKind() {
return kind;
}

@Override
public @NotNull Language getLanguage() {
return null;
}

@Override
public PsiElement @NotNull [] getChildren() {
return new PsiElement[0];
}

@Override
public PsiElement getParent() {
return null;
}

@Override
public TextRange getTextRange() {
return textRange;
}

@Override
public int getStartOffsetInParent() {
return 0;
}

@Override
public int getTextLength() {
return 0;
}

@Override
public @Nullable PsiElement findElementAt(int offset) {
return null;
}

@Override
public int getTextOffset() {
return 0;
}

@Override
public @NlsSafe String getText() {
return null;
}

@Override
public char @NotNull [] textToCharArray() {
return new char[0];
}

@Override
public ASTNode getNode() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,41 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.Consumer;
import org.eclipse.lsp4j.DocumentHighlightKind;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.logging.Logger;

public class LSPHighlightUsagesHandler extends HighlightUsagesHandlerBase<PsiElement> {
public class LSPHighlightUsagesHandler extends HighlightUsagesHandlerBase<LSPHighlightPsiElement> {
private static final Logger LOGGER = Logger.getLogger(LSPHighlightUsagesHandler.class.getName());
private final List<PsiElement> targets;
private final List<LSPHighlightPsiElement> targets;

public LSPHighlightUsagesHandler(Editor editor, PsiFile file, List<PsiElement> targets) {
public LSPHighlightUsagesHandler(Editor editor, PsiFile file, List<LSPHighlightPsiElement> targets) {
super(editor, file);
this.targets = targets;
}

@Override
public @NotNull List<PsiElement> getTargets() {
public @NotNull List<LSPHighlightPsiElement> getTargets() {
return targets;
}

@Override
protected void selectTargets(@NotNull List<? extends PsiElement> targets,
@NotNull Consumer<? super List<? extends PsiElement>> selectionConsumer) {
protected void selectTargets(@NotNull List<? extends LSPHighlightPsiElement> targets,
@NotNull Consumer<? super List<? extends LSPHighlightPsiElement>> selectionConsumer) {
selectionConsumer.consume(targets);
}

@Override
public void computeUsages(@NotNull List<? extends PsiElement> targets) {
targets.forEach(target -> myReadUsages.add(target.getTextRange()));
public void computeUsages(@NotNull List<? extends LSPHighlightPsiElement> targets) {
targets.forEach(target ->
{
if (target.getKind() == DocumentHighlightKind.Read) {
myReadUsages.add(target.getTextRange());
} else {
myWriteUsages.add(target.getTextRange());
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactory;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils;
Expand Down Expand Up @@ -43,12 +45,12 @@ public class LSPHighlightUsagesHandlerFactory implements HighlightUsagesHandlerF

@Override
public @Nullable HighlightUsagesHandlerBase createHighlightUsagesHandler(@NotNull Editor editor, @NotNull PsiFile file) {
List<PsiElement> targets = getTargets(editor, file);
List<LSPHighlightPsiElement> targets = getTargets(editor, file);
return targets.isEmpty()?null:new LSPHighlightUsagesHandler(editor, file, targets);
}

private List<PsiElement> getTargets(Editor editor, PsiFile file) {
List<PsiElement> elements = new ArrayList<>();
private List<LSPHighlightPsiElement> getTargets(Editor editor, PsiFile file) {
List<LSPHighlightPsiElement> elements = new ArrayList<>();
try {
int offset = TargetElementUtil.adjustOffset(file, editor.getDocument(), editor.getCaretModel().getOffset());
Document document = editor.getDocument();
Expand Down Expand Up @@ -76,13 +78,14 @@ private List<PsiElement> getTargets(Editor editor, PsiFile file) {
ProgressManager.checkCanceled();
DocumentHighlight highlight = highlights.poll(25, TimeUnit.MILLISECONDS);
if (highlight != null) {
int highlightOffset = LSPIJUtils.toOffset(highlight.getRange().getStart(), document);
PsiElement element = file.findElementAt(highlightOffset);
if (element != null) {
elements.add(element);
TextRange textRange = LSPIJUtils.toTextRange(highlight.getRange(), document);
if (textRange != null) {
elements.add(new LSPHighlightPsiElement(textRange, highlight.getKind()));
}
}
}
} catch (ProcessCanceledException cancellation){
throw cancellation;
} catch (InterruptedException e) {
LOGGER.log(Level.WARNING, e, e::getLocalizedMessage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils;
import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileWrapper;
import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
Expand All @@ -46,11 +49,14 @@
public class LSPTextHover extends DocumentationProviderEx implements ExternalDocumentationHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(LSPTextHover.class);


private static final String HEAD = "<head>"; //$NON-NLS-1$

private static final Parser PARSER = Parser.builder().build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().build();

private static final Key<Integer> KEY = new Key<>(LSPTextHover.class.getName());

private PsiElement lastElement;
private int lastOffset = -1;
private CompletableFuture<List<Hover>> lspRequest;
Expand Down Expand Up @@ -99,6 +105,12 @@ private static void appendAsHexString(StringBuilder buffer, int intValue) {
buffer.append(hexValue);
}

@Override
public @Nullable PsiElement getCustomDocumentationElement(@NotNull Editor editor, @NotNull PsiFile file, @Nullable PsiElement contextElement, int targetOffset) {
editor.putUserData(KEY, targetOffset);
return null;
}

@Nullable
@Override
public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
Expand All @@ -108,35 +120,44 @@ public String getQuickNavigateInfo(PsiElement element, PsiElement originalElemen
@Nullable
@Override
public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) {

if (originalElement == null || !Objects.equals(element.getContainingFile(), originalElement.getContainingFile())) {
return null;
}
Editor editor = LSPIJUtils.editorForElement(element);
if (editor == null) {
return null;
}
int targetOffset = getTargetOffset(originalElement);
initiateHoverRequest(element, targetOffset);
try {
String result = lspRequest.get(500, TimeUnit.MILLISECONDS).stream()
.filter(Objects::nonNull)
.map(LSPTextHover::getHoverString)
.filter(Objects::nonNull)
.collect(Collectors.joining("\n\n")) //$NON-NLS-1$
.trim();
if (!result.isEmpty()) {
return styleHtml(editor, RENDERER.render(PARSER.parse(result)));
int targetOffset = getTargetOffset(editor, originalElement);
initiateHoverRequest(element, targetOffset);
try {
String result = lspRequest.get(500, TimeUnit.MILLISECONDS).stream()
.filter(Objects::nonNull)
.map(LSPTextHover::getHoverString)
.filter(Objects::nonNull)
.collect(Collectors.joining("\n\n")) //$NON-NLS-1$
.trim();
if (!result.isEmpty()) {
return styleHtml(editor, RENDERER.render(PARSER.parse(result)));
}
} catch (ExecutionException | TimeoutException e) {
LOGGER.warn(e.getLocalizedMessage(), e);
} catch (InterruptedException e) {
LOGGER.warn(e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
}
} catch (ExecutionException | TimeoutException e) {
LOGGER.warn(e.getLocalizedMessage(), e);
} catch (InterruptedException e) {
LOGGER.warn(e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
} finally {
editor.putUserData(KEY, null);
}
return null;
}

private int getTargetOffset(PsiElement originalElement) {
private int getTargetOffset(Editor editor, PsiElement originalElement) {
Integer targetOffset = editor.getUserData(KEY);
if (targetOffset != null) {
return targetOffset;
}
int startOffset = originalElement.getTextOffset();
int textLength = originalElement.getTextLength();
return startOffset + textLength / 2;
Expand Down Expand Up @@ -183,7 +204,7 @@ private int getTargetOffset(PsiElement originalElement) {
private void initiateHoverRequest(PsiElement element, int offset) {
PsiDocumentManager manager = PsiDocumentManager.getInstance(element.getProject());
final Document document = manager.getDocument(element.getContainingFile());
if (offset != -1 && (this.lspRequest == null || !element.equals(this.lastElement) || offset != this.lastOffset)) {
if (offset != -1 && (this.lspRequest == null || offset != this.lastOffset)) {
this.lastElement = element;
this.lastOffset = offset;
this.lspRequest = LanguageServiceAccessor.getInstance(element.getProject())
Expand Down
Loading

0 comments on commit 1a9c902

Please sign in to comment.