diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileWrapper.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileWrapper.java index 64ca86cb0..2b8321421 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileWrapper.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileWrapper.java @@ -23,9 +23,10 @@ import com.intellij.psi.PsiElement; import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.LSPDiagnosticsForServer; import com.redhat.devtools.intellij.lsp4ij.operations.documentLink.LSPDocumentLinkForServer; -import com.redhat.devtools.intellij.lsp4ij.operations.hover.LSPTextHoverForFile; +import com.redhat.devtools.intellij.lsp4ij.operations.documentation.LSPTextHoverForFile; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DocumentLink; +import org.eclipse.lsp4j.MarkupContent; import java.util.List; import java.util.Collection; @@ -109,7 +110,7 @@ public Collection getAllDocumentLink() { } - public String getHoverContent(PsiElement element, int targetOffset, Editor editor) { + public List getHoverContent(PsiElement element, int targetOffset, Editor editor) { return hover.getHoverContent(element, targetOffset, editor); } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java index 82a916ea2..7f63c59be 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java @@ -53,7 +53,7 @@ public class SupportedFeatures { .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.setResolveSupport(new CompletionItemResolveSupportCapabilities(List.of("documentation" /*, "detail", "additionalTextEdits" */))); CompletionCapabilities completionCapabilities = new CompletionCapabilities(completionItemCapabilities); completionCapabilities.setCompletionList(new CompletionListCapabilities(List.of("editRange"))); textDocumentClientCapabilities.setCompletion(completionCapabilities); diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java index 93011bd23..11eeb7bd8 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java @@ -69,20 +69,20 @@ public void fillCompletionVariants(@NotNull CompletionParameters parameters, @No async processing is occuring on a separate thread. */ CompletionParams params = LSPIJUtils.toCompletionParams(LSPIJUtils.toUri(document), offset, document); - BlockingDeque, CompletionList>, LanguageServer>> proposals = new LinkedBlockingDeque<>(); + BlockingDeque, CompletionList>, LanguageServerItem>> proposals = new LinkedBlockingDeque<>(); CompletableFuture future = completionLanguageServersFuture .thenComposeAsync(languageServers -> cancellationSupport.execute( CompletableFuture.allOf(languageServers.stream() .map(languageServer -> cancellationSupport.execute(languageServer.getServer().getTextDocumentService().completion(params)) - .thenAcceptAsync(completion -> proposals.add(new Pair<>(completion, languageServer.getServer())))) + .thenAcceptAsync(completion -> proposals.add(new Pair<>(completion, languageServer)))) .toArray(CompletableFuture[]::new)))); ProgressManager.checkCanceled(); while (!future.isDone() || !proposals.isEmpty()) { ProgressManager.checkCanceled(); - Pair, CompletionList>, LanguageServer> pair = proposals.poll(25, TimeUnit.MILLISECONDS); + Pair, CompletionList>, LanguageServerItem> pair = proposals.poll(25, TimeUnit.MILLISECONDS); if (pair != null) { Either, CompletionList> completion = pair.getFirst(); if (completion != null) { @@ -101,7 +101,7 @@ public void fillCompletionVariants(@NotNull CompletionParameters parameters, @No } private void addCompletionItems(PsiFile file, Editor editor, CompletionPrefix completionPrefix, Either, - CompletionList> completion, LanguageServer languageServer, @NotNull CompletionResultSet result, CancellationSupport cancellationSupport) { + CompletionList> completion, LanguageServerItem languageServer, @NotNull CompletionResultSet result, CancellationSupport cancellationSupport) { CompletionItemDefaults itemDefaults = null; List items = null; if (completion.isLeft()) { @@ -138,7 +138,7 @@ private void addCompletionItems(PsiFile file, Editor editor, CompletionPrefix co private static LSPCompletionProposal createLookupItem(PsiFile file, Editor editor, int offset, CompletionItem item, - CompletionItemDefaults itemDefaults, LanguageServer languageServer) { + CompletionItemDefaults itemDefaults, LanguageServerItem languageServer) { // Update text edit range with item defaults if needed updateWithItemDefaults(item, itemDefaults); return new LSPCompletionProposal(file, editor, offset, item, languageServer); @@ -179,4 +179,4 @@ private static CompletableFuture> initiateLanguageServe return false; }); } -} +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionProposal.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionProposal.java index 1115973f5..5f7f3b184 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionProposal.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionProposal.java @@ -21,24 +21,27 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorModificationUtil; -import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.intellij.lsp4ij.LanguageServerItem; import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; import com.redhat.devtools.intellij.lsp4ij.command.internal.CommandExecutor; import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetIndentOptions; import org.apache.commons.lang.StringUtils; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.LanguageServer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import static com.redhat.devtools.intellij.lsp4ij.operations.completion.CompletionProposalTools.createLspIndentOptions; import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetVariableConstants.*; @@ -53,12 +56,14 @@ public class LSPCompletionProposal extends LookupElement { private final CompletionItem item; private final int initialOffset; private final PsiFile file; + private final Boolean supportResolveCompletion; private int currentOffset; private int bestOffset; private final Editor editor; - private final LanguageServer languageServer; + private final LanguageServerItem languageServer; + private String documentation; - public LSPCompletionProposal(PsiFile file, Editor editor, int offset, CompletionItem item, LanguageServer languageServer) { + public LSPCompletionProposal(PsiFile file, Editor editor, int offset, CompletionItem item, LanguageServerItem languageServer) { this.file = file; this.item = item; this.editor = editor; @@ -66,6 +71,8 @@ public LSPCompletionProposal(PsiFile file, Editor editor, int offset, Completion this.initialOffset = offset; this.currentOffset = offset; this.bestOffset = getPrefixCompletionStart(editor.getDocument(), offset); + ServerCapabilities serverCapabilities = languageServer.getServerWrapper().getServerCapabilities(); + this.supportResolveCompletion = serverCapabilities != null && serverCapabilities.getCompletionProvider() != null && serverCapabilities.getCompletionProvider().getResolveProvider(); putUserData(CodeCompletionHandlerBase.DIRECT_INSERTION, true); } @@ -274,7 +281,7 @@ private void executeCustomCommand(@NotNull Command command, Document document) { Project project = editor.getProject(); // Execute custom command of the completion item. LanguageServiceAccessor.getInstance(project) - .resolveServerDefinition(languageServer).map(definition -> definition.id) + .resolveServerDefinition(languageServer.getServer()).map(definition -> definition.id) .ifPresent(id -> { CommandExecutor.executeCommand(project, command, document, id); }); @@ -347,4 +354,38 @@ public CompletionItem getItem() { } } + public MarkupContent getDocumentation() { + if (item.getDocumentation() == null && supportResolveCompletion) { + try { + CompletionItem resolved = languageServer.getServer() + .getTextDocumentService() + .resolveCompletionItem(item) + .get(1000, TimeUnit.MILLISECONDS); + if (resolved != null) { + item.setDocumentation(resolved.getDocumentation()); + } + } catch (ExecutionException e) { + if (!(e.getCause() instanceof CancellationException)) { + LOGGER.warn(e.getLocalizedMessage(), e); + } + } catch (TimeoutException e) { + LOGGER.warn(e.getLocalizedMessage(), e); + } catch (InterruptedException e) { + LOGGER.warn(e.getLocalizedMessage(), e); + Thread.currentThread().interrupt(); + } + } + return getDocumentation(item.getDocumentation()); + } + + private static MarkupContent getDocumentation(Either documentation) { + if (documentation == null) { + return null; + } + if (documentation.isLeft()) { + String content = documentation.getLeft(); + return new MarkupContent(content, MarkupKind.PLAINTEXT); + } + return documentation.getRight(); + } } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPDocumentationProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPDocumentationProvider.java new file mode 100644 index 000000000..a686ad8b2 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPDocumentationProvider.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.intellij.lsp4ij.operations.documentation; + +import com.intellij.lang.documentation.DocumentationProviderEx; +import com.intellij.lang.documentation.ExternalDocumentationHandler; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +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.operations.completion.LSPCompletionProposal; +import com.vladsch.flexmark.html.HtmlRenderer; +import com.vladsch.flexmark.parser.Parser; +import org.eclipse.lsp4j.MarkupContent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * {@link DocumentationProviderEx} implementation for LSP to support: + * + *
    + *
  • textDocument/hover
  • + *
  • documentation for completion item
  • + *
. + */ +public class LSPDocumentationProvider extends DocumentationProviderEx implements ExternalDocumentationHandler { + + private static final Parser PARSER = Parser.builder().build(); + private static final HtmlRenderer RENDERER = HtmlRenderer.builder().build(); + + private static final Key TARGET_OFFSET_KEY = new Key<>(LSPDocumentationProvider.class.getName()); + + @Nullable + @Override + public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { + return generateDoc(element, originalElement); + } + + @Override + public @Nullable PsiElement getCustomDocumentationElement(@NotNull Editor editor, @NotNull PsiFile file, @Nullable PsiElement contextElement, int targetOffset) { + if (contextElement != null) { + // Store the offset where the hover has been triggered + contextElement.putUserData(TARGET_OFFSET_KEY, targetOffset); + } + return super.getCustomDocumentationElement(editor, file, contextElement, targetOffset); + } + + @Nullable + @Override + public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { + try { + Project project = element.getProject(); + if (project.isDisposed()) { + return null; + } + Editor editor = LSPIJUtils.editorForElement(element); + if (editor == null) { + return null; + } + List result = getMarkupContents(element, originalElement); + if (result == null || result.isEmpty()) { + return null; + } + String s = result + .stream() + .map(m -> m.getValue()) + .collect(Collectors.joining("\n\n")); + return styleHtml(editor, RENDERER.render(PARSER.parse(s))); + } finally { + if (originalElement != null) { + originalElement.putUserData(TARGET_OFFSET_KEY, null); + } + } + } + + @Nullable + public List getMarkupContents(PsiElement element, @Nullable PsiElement originalElement) { + if (element instanceof LSPPsiElementForLookupItem) { + // Show documentation for a given completion item in the "documentation popup" (see IJ Completion setting) + // (LSP textDocument/completion request) + return ((LSPPsiElementForLookupItem) element).getDocumentation(); + } + if (originalElement == null || !Objects.equals(element.getContainingFile(), originalElement.getContainingFile())) { + return null; + } + Editor editor = LSPIJUtils.editorForElement(element); + if (editor == null) { + return null; + } + + // Show documentation for a hovered element (LSP textDocument/hover request). + VirtualFile file = originalElement.getContainingFile().getVirtualFile(); + if (LSPVirtualFileWrapper.hasWrapper(file)) { + int targetOffset = getTargetOffset(originalElement); + return LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file).getHoverContent(element, targetOffset, editor); + } + return null; + } + + private static int getTargetOffset(PsiElement originalElement) { + Integer targetOffset = originalElement.getUserData(TARGET_OFFSET_KEY); + if (targetOffset != null) { + return targetOffset; + } + int startOffset = originalElement.getTextOffset(); + int textLength = originalElement.getTextLength(); + return startOffset + textLength / 2; + } + + @Nullable + @Override + public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) { + if (object instanceof LSPCompletionProposal) { + MarkupContent documentation = ((LSPCompletionProposal) object).getDocumentation(); + if (documentation != null) { + return new LSPPsiElementForLookupItem(documentation, psiManager, element); + } + } + return null; + } + + @Nullable + @Override + public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) { + return null; + } + + @Override + public boolean handleExternal(PsiElement element, PsiElement originalElement) { + return false; + } + + @Override + public boolean handleExternalLink(PsiManager psiManager, String link, PsiElement context) { + VirtualFile file = LSPIJUtils.findResourceFor(link); + if (file != null) { + FileEditorManager.getInstance(psiManager.getProject()).openFile(file, true, true); + return true; + } + return false; + } + + @Override + public boolean canFetchDocumentationLink(String link) { + return false; + } + + @Override + public @NotNull String fetchExternalDocumentation(@NotNull String link, @Nullable PsiElement element) { + return null; + } + + + public static String styleHtml(Editor editor, String htmlBody) { + if (htmlBody == null || htmlBody.isEmpty()) { + return htmlBody; + } + Color background = editor.getColorsScheme().getDefaultBackground(); + Color foreground = editor.getColorsScheme().getDefaultForeground(); + + StringBuilder html = new StringBuilder("") + .append(htmlBody) + .append(""); + return html.toString(); + } + + private static String toHTMLrgb(Color rgb) { + StringBuilder builder = new StringBuilder(7); + builder.append('#'); + appendAsHexString(builder, rgb.getRed()); + appendAsHexString(builder, rgb.getGreen()); + appendAsHexString(builder, rgb.getBlue()); + return builder.toString(); + } + + private static void appendAsHexString(StringBuilder buffer, int intValue) { + String hexValue = Integer.toHexString(intValue); + if (hexValue.length() == 1) { + buffer.append('0'); + } + buffer.append(hexValue); + } + +} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPPsiElementForLookupItem.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPPsiElementForLookupItem.java new file mode 100644 index 000000000..bcedbe908 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPPsiElementForLookupItem.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.lsp4ij.operations.documentation; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.FakePsiElement; +import org.eclipse.lsp4j.MarkupContent; + +import java.util.Collections; +import java.util.List; + +/** + * Implement a fake {@link PsiElement} which stores the completion item documentation. + */ +public class LSPPsiElementForLookupItem extends FakePsiElement { + private final MarkupContent documentation; + private final PsiManager psiManager; + private final PsiElement element; + + public LSPPsiElementForLookupItem(MarkupContent documentation, PsiManager psiManager, PsiElement element) { + this.psiManager = psiManager; + this.documentation = documentation; + this.element = element; + } + + public List getDocumentation() { + return documentation != null ? Collections.singletonList(documentation) : null; + } + + public PsiElement getNavigationElement() { + return element; + } + + @Override + public PsiElement getParent() { + return getNavigationElement(); + } + + @Override + public String getName() { + return ""; + } + + @Override + public PsiFile getContainingFile() { + return element.getContainingFile(); + } + + + @Override + public PsiManager getManager() { + return psiManager; + } + +} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/hover/LSPTextHoverForFile.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPTextHoverForFile.java similarity index 62% rename from src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/hover/LSPTextHoverForFile.java rename to src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPTextHoverForFile.java index a5e4f9e8c..9ef79c55c 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/hover/LSPTextHoverForFile.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPTextHoverForFile.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.operations.hover; +package com.redhat.devtools.intellij.lsp4ij.operations.documentation; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; @@ -17,24 +17,15 @@ import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -import com.vladsch.flexmark.html.HtmlRenderer; -import com.vladsch.flexmark.parser.Parser; -import org.eclipse.lsp4j.Hover; -import org.eclipse.lsp4j.MarkedString; -import org.eclipse.lsp4j.MarkupContent; -import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.awt.*; import java.util.List; import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.concurrent.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -44,29 +35,28 @@ public class LSPTextHoverForFile { private static final Logger LOGGER = LoggerFactory.getLogger(LSPTextHoverForFile.class); - private static final Parser PARSER = Parser.builder().build(); - private static final HtmlRenderer RENDERER = HtmlRenderer.builder().build(); - private PsiElement lastElement; private int lastOffset = -1; private CompletableFuture> lspRequest; private CancellationSupport previousCancellationSupport; - public String getHoverContent(PsiElement element, int targetOffset, Editor editor) { + public List getHoverContent(PsiElement element, int targetOffset, Editor editor) { initiateHoverRequest(element, targetOffset); try { - String result = lspRequest.get(500, TimeUnit.MILLISECONDS).stream() + List result = lspRequest + .get(500, TimeUnit.MILLISECONDS).stream() .filter(Objects::nonNull) .map(LSPTextHoverForFile::getHoverString) .filter(Objects::nonNull) - .collect(Collectors.joining("\n\n")) //$NON-NLS-1$ - .trim(); - if (!result.isEmpty()) { - return styleHtml(editor, RENDERER.render(PARSER.parse(result))); - } + .collect(Collectors.toList()); // The LSP hover request are finished, don't need to cancel the previous LSP requests. previousCancellationSupport = null; - } catch (ExecutionException | TimeoutException e) { + return result; + } catch (ExecutionException e) { + if (!(e.getCause() instanceof CancellationException)) { + LOGGER.warn(e.getLocalizedMessage(), e); + } + } catch (TimeoutException e) { LOGGER.warn(e.getLocalizedMessage(), e); } catch (InterruptedException e) { LOGGER.warn(e.getLocalizedMessage(), e); @@ -82,10 +72,9 @@ public String getHoverContent(PsiElement element, int targetOffset, Editor edito * @param element the PSI element. * @param offset the target offset. */ - private void initiateHoverRequest(PsiElement element, int offset) { if (this.previousCancellationSupport != null) { - // The prvious LSP hover request is not finished,cancel it + // The previous LSP hover request is not finished,cancel it this.previousCancellationSupport.cancel(); } PsiDocumentManager manager = PsiDocumentManager.getInstance(element.getProject()); @@ -110,14 +99,14 @@ private void initiateHoverRequest(PsiElement element, int offset) { } } - private static @Nullable String getHoverString(Hover hover) { + private static @Nullable MarkupContent getHoverString(Hover hover) { Either>, MarkupContent> hoverContent = hover.getContents(); if (hoverContent.isLeft()) { List> contents = hoverContent.getLeft(); if (contents == null || contents.isEmpty()) { return null; } - return contents.stream().map(content -> { + String s = contents.stream().map(content -> { if (content.isLeft()) { return content.getLeft(); } else if (content.isRight()) { @@ -134,9 +123,10 @@ private void initiateHoverRequest(PsiElement element, int offset) { } else { return ""; //$NON-NLS-1$ } - }).filter(((Predicate) String::isEmpty).negate()).collect(Collectors.joining("\n\n")); //$NON-NLS-1$ ) + }).filter(((Predicate) String::isEmpty).negate()).collect(Collectors.joining("\n\n")); + return new MarkupContent(s, MarkupKind.PLAINTEXT); } else { - return hoverContent.getRight().getValue(); + return hoverContent.getRight(); } } @@ -144,45 +134,4 @@ private static boolean isHoverCapable(ServerCapabilities capabilities) { return (capabilities.getHoverProvider().isLeft() && capabilities.getHoverProvider().getLeft()) || capabilities.getHoverProvider().isRight(); } - - public static String styleHtml(Editor editor, String html) { - if (html == null || html.isEmpty()) { - return html; - } - Color background = editor.getColorsScheme().getDefaultBackground(); - Color foreground = editor.getColorsScheme().getDefaultForeground(); - // put CSS styling to match Eclipse style - String style = ""; //$NON-NLS-1$ - - /*int headIndex = html.indexOf(HEAD); - StringBuilder builder = new StringBuilder(html.length() + style.length()); - builder.append(html.substring(0, headIndex + HEAD.length())); - builder.append(style); - builder.append(html.substring(headIndex + HEAD.length())); - return builder.toString();*/ - StringBuilder builder = new StringBuilder(style); - builder.append(html).append(""); - return builder.toString(); - } - - private static String toHTMLrgb(Color rgb) { - StringBuilder builder = new StringBuilder(7); - builder.append('#'); - appendAsHexString(builder, rgb.getRed()); - appendAsHexString(builder, rgb.getGreen()); - appendAsHexString(builder, rgb.getBlue()); - return builder.toString(); - } - - private static void appendAsHexString(StringBuilder buffer, int intValue) { - String hexValue = Integer.toHexString(intValue); - if (hexValue.length() == 1) { - buffer.append('0'); - } - buffer.append(hexValue); - } - } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java index 909c57ca0..14d19ceed 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java @@ -22,7 +22,6 @@ import java.util.logging.Logger; public class LSPHighlightUsagesHandler extends HighlightUsagesHandlerBase { - private static final Logger LOGGER = Logger.getLogger(LSPHighlightUsagesHandler.class.getName()); private final List targets; public LSPHighlightUsagesHandler(Editor editor, PsiFile file, List targets) { diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/hover/LSPTextHover.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/hover/LSPTextHover.java deleted file mode 100644 index a24b3b118..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/hover/LSPTextHover.java +++ /dev/null @@ -1,124 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at https://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.operations.hover; - -import com.intellij.lang.documentation.DocumentationProviderEx; -import com.intellij.lang.documentation.ExternalDocumentationHandler; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.project.Project; -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.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -import com.vladsch.flexmark.html.HtmlRenderer; -import com.vladsch.flexmark.parser.Parser; -import org.eclipse.lsp4j.Hover; -import org.eclipse.lsp4j.MarkedString; -import org.eclipse.lsp4j.MarkupContent; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.*; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -/** - * LSP testDocument/hover support. - * - */ -public class LSPTextHover extends DocumentationProviderEx implements ExternalDocumentationHandler { - - @Nullable - @Override - public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { - return generateDoc(element, originalElement); - } - - @Nullable - @Override - public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { - Project project = element.getProject(); - if (project.isDisposed()) { - return null; - } - if (originalElement == null || !Objects.equals(element.getContainingFile(), originalElement.getContainingFile())) { - return null; - } - Editor editor = LSPIJUtils.editorForElement(element); - if (editor == null) { - return null; - } - - VirtualFile file = originalElement.getContainingFile().getVirtualFile(); - int targetOffset = getTargetOffset(originalElement); - return LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file).getHoverContent(element, targetOffset, editor); - } - - private static int getTargetOffset(PsiElement originalElement) { - int startOffset = originalElement.getTextOffset(); - int textLength = originalElement.getTextLength(); - return startOffset + textLength / 2; - } - - @Nullable - @Override - public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) { - return null; - } - - @Nullable - @Override - public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) { - return null; - } - - @Override - public boolean handleExternal(PsiElement element, PsiElement originalElement) { - return false; - } - - @Override - public boolean handleExternalLink(PsiManager psiManager, String link, PsiElement context) { - VirtualFile file = LSPIJUtils.findResourceFor(link); - if (file != null) { - FileEditorManager.getInstance(psiManager.getProject()).openFile(file, true, true); - return true; - } - return false; - } - - @Override - public boolean canFetchDocumentationLink(String link) { - return false; - } - - @Override - public @NotNull String fetchExternalDocumentation(@NotNull String link, @Nullable PsiElement element) { - return null; - } -} diff --git a/src/main/resources/META-INF/lsp4ij-quarkus.xml b/src/main/resources/META-INF/lsp4ij-quarkus.xml index 9e149efbc..56fb1dd12 100644 --- a/src/main/resources/META-INF/lsp4ij-quarkus.xml +++ b/src/main/resources/META-INF/lsp4ij-quarkus.xml @@ -47,10 +47,10 @@ - + - + diff --git a/src/main/resources/META-INF/lsp4ij-qute.xml b/src/main/resources/META-INF/lsp4ij-qute.xml index 4d7b16516..2e2e43b31 100644 --- a/src/main/resources/META-INF/lsp4ij-qute.xml +++ b/src/main/resources/META-INF/lsp4ij-qute.xml @@ -24,11 +24,11 @@ - - - - - + + + + +