Skip to content

Commit

Permalink
[Intellij] Add hover support. Fixes #278
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Apr 6, 2021
1 parent 641b8d5 commit 8ad3798
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 8 deletions.
3 changes: 2 additions & 1 deletion robotframework-intellij/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
compile group: 'org.eclipse.lsp4j', name: 'org.eclipse.lsp4j', version: '0.12.0'
compile 'org.markdownj:markdownj-core:0.4'
}

// See https://github.com/JetBrains/gradle-intellij-plugin/
Expand All @@ -49,7 +50,7 @@ intellij {
patchPluginXml {
changeNotes """
<p>
Release 0.11.1
Release 0.12.0
</p>
<ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public class FeatureDefinition {
int endOffset = EditorUtils.LSPPosToOffset(targetDocument, targetRange.getEnd());

String text = targetDocument.getText(new TextRange(startOffset, endOffset));
return new LSPGenericPsiElement(project, targetPsiFile, text, startOffset, endOffset);
return new LSPGenericPsiElement(project, targetPsiFile, text, startOffset, endOffset, targetRange, textDocumentIdentifier, pos, languageDefinition);
} catch (ProcessCanceledException e) {
// If it was cancelled, just ignore it (don't log).
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package robocorp.lsp.intellij;

import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiElement;
import com.petebevin.markdown.MarkdownProcessor;
import org.apache.commons.lang.StringEscapeUtils;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.HoverParams;
import org.eclipse.lsp4j.MarkedString;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.jetbrains.annotations.Nullable;
import robocorp.lsp.psi.LSPGenericPsiElement;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;


public class FeatureHover implements DocumentationProvider {

private static final Logger LOG = Logger.getInstance(FeatureHover.class);

public FeatureHover() {
}

@Override
public @Nullable @NlsSafe String generateDoc(PsiElement element, @Nullable PsiElement originalElement) {
if (element instanceof LSPGenericPsiElement) {
LSPGenericPsiElement lspGenericPsiElement = (LSPGenericPsiElement) element;
Project project = lspGenericPsiElement.getProject();
LanguageServerDefinition languageDefinition = lspGenericPsiElement.languageDefinition;
LanguageServerManager languageServerManager = LanguageServerManager.getInstance(languageDefinition);

String basePath = project.getBasePath();
if (basePath == null) {
return null;
}

try {
LanguageServerCommunication comm = languageServerManager.getLanguageServerCommunication(languageDefinition.ext.iterator().next(), basePath, project);
if (comm == null) {
return null;
}
CompletableFuture<Hover> future = comm.hover(new HoverParams(lspGenericPsiElement.originalId, lspGenericPsiElement.originalPos));
if (future == null) {
return null;
}
Hover hover = future.get(Timeouts.getHoverTimeout(), TimeUnit.SECONDS);
Either<List<Either<String, MarkedString>>, MarkupContent> contents = hover.getContents();
if (contents.isLeft()) {
List<Either<String, MarkedString>> left = contents.getLeft();
Iterator<Either<String, MarkedString>> iterator = left.iterator();
while (iterator.hasNext()) {
FastStringBuffer buf = new FastStringBuffer();
Either<String, MarkedString> next = iterator.next();
if (next.isLeft()) {
buf.append(StringEscapeUtils.escapeHtml(next.getLeft()));
} else {
MarkedString right = next.getRight();
// Could be better (but it's deprecated anyways, so, don't bother).
buf.append(StringEscapeUtils.escapeHtml(right.getValue()));
}
buf.append('\n');
}

} else if (contents.isRight()) {
MarkupContent right = contents.getRight();
if ("markdown".equals(right.getKind())) {
String mdContent = right.getValue();
MarkdownProcessor processor = new MarkdownProcessor();
return processor.markdown(mdContent);
} else {
return StringEscapeUtils.escapeHtml(right.getValue());
}
}
} catch (ProcessCanceledException e) {
// If it was cancelled, just ignore it (don't log).
} catch (Exception e) {
LOG.error(e);
}
}
return null;
}

@Override
public @Nullable String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
if (element != null) {
return element.toString();
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -534,4 +534,14 @@ public CompletableFuture<SemanticTokens> getSemanticTokens(SemanticTokensParams
return languageServer.getTextDocumentService().semanticTokensFull(params);
}

public @Nullable CompletableFuture<Hover> hover(HoverParams params) {
LanguageServer languageServer = obtainSynchronizedLanguageServer();

if (languageServer == null) {
LOG.info("Unable to get hover: disconnected.");
return null;
}
return languageServer.getTextDocumentService().hover(params);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
public class Timeouts {
// Initial values (after the first one succeeds, the timeout becomes lower).
private static long definitionTimeout = 5;
private static long hoverTimeout = 5;

private static long symbolsTimeout = 8;
private static long completionsTimeout = 8;

Expand All @@ -24,4 +26,11 @@ public static long getCompletionTimeout() {
completionsTimeout = 3;
return ret;
}

public static long getHoverTimeout() {
long ret = hoverTimeout;
hoverTimeout = 3;
return ret;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileTypes.PlainTextLanguage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiElementBase;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import robocorp.lsp.intellij.LanguageServerDefinition;

public class LSPGenericPsiElement extends PsiElementBase implements PsiNameIdentifierOwner, NavigatablePsiElement {

Expand Down Expand Up @@ -39,13 +42,22 @@ protected TextRange calculateDefaultRangeInElement() {

public final int startOffset;
public final int endOffset;
public final Range startRange;

public LSPGenericPsiElement(@NotNull Project project, @NotNull PsiFile file, @NotNull String text, int startOffset, int endOffset) {
public final TextDocumentIdentifier originalId;
public final Position originalPos;
public final LanguageServerDefinition languageDefinition;

public LSPGenericPsiElement(@NotNull Project project, @NotNull PsiFile file, @NotNull String text, int startOffset, int endOffset, Range startRange, TextDocumentIdentifier originalId, Position originalPos, LanguageServerDefinition languageDefinition) {
this.project = project;
this.file = file;
this.text = text;
this.startOffset = startOffset;
this.endOffset = endOffset;
this.startRange = startRange;
this.originalId = originalId;
this.originalPos = originalPos;
this.languageDefinition = languageDefinition;
}

@Override
Expand All @@ -61,8 +73,7 @@ public PsiReference getReference() {
@Override
@NotNull
public Language getLanguage() {
// XXX: Can we do better than this?
return PlainTextLanguage.INSTANCE;
return file.getLanguage();
}

@Override
Expand All @@ -78,9 +89,9 @@ public PsiElement getParent() {
@Override
public String toString() {
if (text.length() > 0) {
return text + " at: " + file.getName() + " offset: " + startOffset;
return text + " at: " + file.getName() + " line: " + startRange.getStart().getLine() + 1;
}
return file.getName() + " offset: " + startOffset;
return file.getName() + " line: " + startRange.getStart().getLine() + 1;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
<projectService serviceImplementation="robocorp.robot.intellij.RobotProjectPreferences"/>

<notificationGroup id="Robot Framework Language Server" displayType="BALLOON"/>
<lang.documentationProvider language="RobotFramework"
implementationClass="robocorp.lsp.intellij.FeatureHover"/>
</extensions>

</idea-plugin>

0 comments on commit 8ad3798

Please sign in to comment.