~damien/jedit-lsp

371b3310e2a2bc5b279569618c02985d84cd3ba1 — Damien Radtke 3 months ago 3706ce8 master v0.1
Improve hover support
3 files changed, 94 insertions(+), 32 deletions(-)

M LSP.props
A lsp/Markdown.java
M lsp/Server.java
M LSP.props => LSP.props +2 -0
@@ 62,6 62,8 @@ messages.lsp.new-server.prompt=Enter a unique name for this server (no whitespac
messages.lsp.new-server.invalid-name=Invalid name for LSP server
messages.lsp.new-server.duplicate=That server already exists, try a different name
messages.lsp.mode-settings-description=<html><b>Assign LSP servers to modes</b><br><br>The configured server will launch automatically when first opening a buffer of the specified mode.
messages.lsp.action-unsupported.title=Action Unsupported
messages.lsp.action-unsupported.message=The language server has indicated that this action is not supported: %s

# Define available LSP servers
lsp.servers=gopls jdtls

A lsp/Markdown.java => lsp/Markdown.java +16 -0
@@ 0,0 1,16 @@
package lsp;

// {{{ imports
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
// }}}

public class Markdown {
	public static String renderAsHTML(String markdown) {
		Parser parser = Parser.builder().build();
		Node document = parser.parse(markdown);
		HtmlRenderer renderer = HtmlRenderer.builder().build();
		return renderer.render(document);
	}
}

M lsp/Server.java => lsp/Server.java +76 -32
@@ 1,6 1,7 @@
package lsp;

// {{{ imports
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Point;


@@ 38,9 39,6 @@ import javax.swing.ScrollPaneConstants;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.eclipse.lsp4j.ClientInfo;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionContext;


@@ 59,16 57,19 @@ import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.FormattingOptions;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.HoverOptions;
import org.eclipse.lsp4j.HoverParams;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.InitializedParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.MarkedString;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.RenameParams;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextEdit;


@@ 357,6 358,16 @@ public class Server {
	 * Executes the lsp.hover action.
	 */
	public void hover(TextArea textArea, Buffer buffer) {
		ServerCapabilities caps = getCapabilities();
		if (caps == null) {
			return;
		}
		Either<Boolean, HoverOptions> capHover = caps.getHoverProvider();
		if ((capHover.isLeft() && !capHover.getLeft()) || (capHover.isRight() && capHover.getRight() == null)) {
			actionUnsupported(textArea, "hover");
			return;
		}

		try {
			TextDocumentIdentifier textDocument = Utils.getDocumentIdentifier(buffer);
			Position position = Utils.getPosition(textArea);


@@ 369,41 380,58 @@ public class Server {
						return;
					}
					log(Log.DEBUG, "Hover: " + result.toString());
					JEditorPane editorPane = new JEditorPane("text/html", "");
					if (result.getContents().isLeft()) {
						log(Log.WARNING, "Hover: MarkedString list not supported.");
						return;
					}
					MarkupContent content = result.getContents().getRight();
					if (content == null) {
						return;
					}
					String text = content.getValue();
					JEditorPane editorPane = new JEditorPane();
					switch (content.getKind()) {
						case "plaintext":
							editorPane.setText(text);
							break;

						case "markdown":
							// render
							Parser parser = Parser.builder().build();
							Node document = parser.parse(text);
							HtmlRenderer renderer = HtmlRenderer.builder().build();
							String rendered = renderer.render(document);
							log(Log.DEBUG, rendered);
							editorPane.setContentType("text/html");
							editorPane.setText(rendered);
							break;

						default:
							log(Log.WARNING, "Hover: Unsupported content kind: " + content.getKind());
						StringBuilder markdownBuilder = new StringBuilder();
						for (Either<String, MarkedString> string : result.getContents().getLeft()) {
							if (string.isLeft()) {
								String text = string.getLeft();
								markdownBuilder.append(text != null ? text : "");
							} else if (string.isRight()) {
								MarkedString markedString = string.getRight();
								if (markedString == null) {
									continue;
								}
								markdownBuilder.append("```");
								if (markedString.getLanguage() != null) {
									markdownBuilder.append(markedString.getLanguage());
								}
								markdownBuilder.append("\n" + markedString.getValue() + "\n```");
							}
						}
						String rendered = Markdown.renderAsHTML(markdownBuilder.toString());
						editorPane.setText(rendered);
					} else {
						MarkupContent content = result.getContents().getRight();
						if (content == null) {
							return;
						}
						String text = content.getValue();
						switch (content.getKind()) {
							case "plaintext":
								editorPane.setText(text);
								break;

							case "markdown":
								// render
								String rendered = Markdown.renderAsHTML(text);
								editorPane.setText(rendered);
								break;

							default:
								log(Log.WARNING, "Hover: Unsupported content kind: " + content.getKind());
								return;
						}
					}

					editorPane.setEditable(false);
					editorPane.addHyperlinkListener(new LinkOpener());

					JScrollPane scrollPane = new JScrollPane(editorPane, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
					JScrollPane scrollPane = new JScrollPane(
						editorPane,
						ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
						ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
					);
					scrollPane.setBorder(BorderFactory.createRaisedBevelBorder());

					ThreadUtilities.runInDispatchThread(() -> {


@@ 418,7 446,6 @@ public class Server {
						Popup popup = PopupFactory.getSharedInstance().getPopup(textArea, scrollPane, (int) pos.getX(), (int) pos.getY());
						LanguageServerPlugin.get().setHoverPopup(popup);
					});
					log(Log.NOTICE, text);
				} catch (Exception e) {
					log(Log.ERROR, "Failed to hover", e);
				}


@@ 736,6 763,23 @@ public class Server {
		return bufferPath.startsWith(workspacePath);
	}

	public ServerCapabilities getCapabilities() {
		if (this.initResult == null) {
			return null;
		}
		return this.initResult.getCapabilities();
	}

	private void actionUnsupported(Component parent, String name) {
		ThreadUtilities.runInDispatchThread(() -> {
			JOptionPane.showInputDialog(
					parent,
					String.format((String) jEdit.getProperty("messages.lsp.action-unsupported.message"), name),
					(String) jEdit.getProperty("messages.lsp.action-unsupported.title"),
					JOptionPane.ERROR_MESSAGE);
		});
	}

	private static class LinkOpener implements HyperlinkListener {
		@Override
		public void hyperlinkUpdate(HyperlinkEvent event) {