Skip to content

Commit

Permalink
docs: Getting started documentation
Browse files Browse the repository at this point in the history
Fixes #24

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Dec 11, 2023
1 parent d1cb1bd commit f0dc06b
Show file tree
Hide file tree
Showing 12 changed files with 372 additions and 11 deletions.
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,22 @@ It currently doesn't provide any useful functionality on its own, but is instead
LSP4IJ provides:

* Language server `extension points` to add any language server:
* `com.redhat.devtools.lsp4ij.server` extension point to define a language server.
* `com.redhat.devtools.lsp4ij.languageMapping` to associate an Intellij language with a language server definition.
* an `LSP Consoles view` to tracks LSP requests, responses and notifications in a console:
* [com.redhat.devtools.lsp4ij.server](./docs/DeveloperGuide.md#declare-server-with-extension-point) extension point to define a language server.
* [com.redhat.devtools.lsp4ij.languageMapping](./docs/DeveloperGuide.md#declare-language-mapping-with-extension-point) to associate an IntelliJ language with a language server definition.
* an [LSP Consoles view](./docs/UserGuide.md#lsp-console) to tracks LSP requests, responses and notifications in a console:

![LSP console](https://github.com/redhat-developer/lsp4ij/blob/HEAD/docs/images/LSPConsole.png?raw=true)
![LSP console](./docs/images/LSPConsole.png)

* a `Language Servers settings page` to configure the LSP trace level, the debug port to use to debug language server:
* a [Language Servers preferences page](./docs/UserGuide.md#language-servers-preferences) to configure the LSP trace level, the debug port to use to debug language server:

![Language Server preferences](./docs/images/LanguageServerPreferences.png)

You can start with:

* [Developer guide](./docs/DeveloperGuide.md) which explains step by step how to integrate a language server in LSP4J in an external IntelliJ plugin.
* [User guide](./docs/UserGuide.md) which explains how to use LSP console and Language server preferences.
* [LSP support](./docs/LSPSupport.md) which explains the LSP support.

![Language Server settings](https://github.com/redhat-developer/lsp4ij/blob/HEAD/docs/images/LanguageServerSettings.png?raw=true)
<!-- Plugin description end -->

## Who is using LSP4IJ?
Expand Down
242 changes: 242 additions & 0 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# Developer guide

This section explains step by step how to add your own LSP language server in your IntelliJ plugin.

## Reference LSP4IJ

### plugin.xml

The first step is to reference LSP4IJ. LSP4IJ uses `com.redhat.devtools.lsp4ij` as plugin Id.

You need [to declare dependency in your plugin.xml](https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html#3-dependency-declaration-in-pluginxml) like this:

```xml
<idea-plugin>
...

<depends>com.redhat.devtools.lsp4ij</depends>

...
</idea-plugin>
```

### Exclude all LSP4J dependencies

LSP4IJ depends on [Eclipse LSP4J](https://github.com/eclipse-lsp4j/lsp4j) (Java binding for the [Language Server Protocol](https://microsoft.github.io/language-server-protocol) and the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol).). It uses a given version of LSPJ and their classes are loaded in the LSP4IJ plugin class loader.

Your IntelliJ Plugin should use `the same LSP4J classes than LSP4IJ` to avoid some `ClassCastException` errors. To do that you need to `exclude all LSP4J dependencies` from your plugin.

Here a sample used in [Quarkus Tools](https://github.com/redhat-developer/intellij-quarkus) in [build.gradle.kts](https://github.com/redhat-developer/intellij-quarkus/blob/main/build.gradle.kts) to exclude LSP4J dependency from the [Qute Language Server](https://github.com/redhat-developer/quarkus-ls/tree/master/qute.ls) which have a dependency to LSP4J:

```
implementation("com.redhat.microprofile:com.redhat.qute.ls:0.17.0) {
exclude("org.eclipse.lsp4j")
}
```

## Declare server


### StreamConnectionProvider Implementation

You need to implement the [StreamConnectionProvider](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/server/StreamConnectionProvider.java) API which manages:

* start of your language server
* returns the input/error stream of LSP requests, responses, notifications.

Generally, the language server is started with a process by using a runtime like Java, NodeJS, etc. In this case you need to extend [ProcessStreamConnectionProvider](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/server/ProcessStreamConnectionProvider.java)

Here a basic sample which starts the `path/to/my/language/server/main.js` language server written in JavaScript with NodeJS runtime "path/to/nodejs/node.exe":

```java
package my.language.server;

import com.redhat.devtools.lsp4ij.server.ProcessStreamConnectionProvider;

import java.util.Arrays;
import java.util.List;

public class MyLanguageServer extends ProcessStreamConnectionProvider {

public MyLanguageServer() {
List<String> commands = Arrays.asList("path/to/nodejs/node.exe", "path/to/my/language/server/main.js");
super.setCommands(commands);
}
}
```

If your language server is written in Java, to build the command, you can use [JavaProcessCommandBuilder](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/server/JavaProcessCommandBuilder.java):

```java
package my.language.server;

import com.intellij.openapi.project.Project;
import com.redhat.devtools.lsp4ij.server.JavaProcessCommandBuilder;
import com.redhat.devtools.lsp4ij.server.ProcessStreamConnectionProvider;

import java.util.Arrays;
import java.util.List;

public class MyLanguageServer extends ProcessStreamConnectionProvider {

public MyLanguageServer(Project project) {
List<String> commands = new JavaProcessCommandBuilder(project, "myLanguageServerId")
.setJar("path/to/my/language/server/main.java")
.create();
super.setCommands(commands);
}
}
```

This builder takes care of filling command with Java runtime and generate the command with debug if the settings of the language server `myLanguageServerId` defines a debug port.

You can see a full sample with [QuteServer](https://github.com/redhat-developer/intellij-quarkus/blob/main/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteServer.java)

### LanguageClientImpl

It is not required but you can override the [LanguageClientImpl](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/client/LanguageClientImpl.java) to for instance:

* some IJ listeners when language client is created
* override some LSP methods

```java
package my.language.server;

import com.intellij.openapi.project.Project;
import com.redhat.devtools.lsp4ij.client.LanguageClientImpl;

public class MyLanguageClient extends LanguageClientImpl {
public MyLanguageClient(Project project) {
super(project);
}
}
```

If your language server manages custom LSP requests, it is advised to extend [IndexAwareLanguageClient](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/client/IndexAwareLanguageClient.java)

You can see a full sample with [QuteLanguageClient](https://github.com/redhat-developer/intellij-quarkus/blob/main/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java)

## Declare server with extension point

The last step is to declare the server in your plugin.xml with `com.redhat.devtools.lsp4ij.server` extension point
to use your `my.language.server.MyLanguageServer` and `my.language.server.MyLanguageClient`:

```xml
<idea-plugin>

<extensions defaultExtensionNs="com.redhat.devtools.lsp4ij">
<server id="myLanguageServerId"
label="My Language Server"
class="my.language.server.MyLanguageServer"
clientImpl="my.language.server.MyLanguageClient"
scope="project">
<description><![CDATA[
Some description written in HTML to display it in LSP consoles and Language Servers settings.
]]>
</description>
</server>
</extensions>

</idea-plugin>
```

Once the declaration is done, your server should appear in the LSP console:

![My LanguageServer in LSP Console](./images/MyLanguageServerInLSPConsole.png)

## Declare language mapping with extension point

Once server is defined, you need to associate an IntelliJ language with the `server` defined by the id attribute
with `com.redhat.devtools.lsp4ij.languageMapping` extension point.

Here a sample which associates the `XML` language with the `myLanguageServerId` server:

```xml
</idea-plugin>

<extensions defaultExtensionNs="com.redhat.devtools.lsp4ij">

<languageMapping language="XML"
serverId="myLanguageServerId" />

</extensions>
```

If the language check is not enough, you can implement a [DocumentMatcher](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/DocumentMatcher.java) to implement some check in Java code.
For instance our language server could map `Java` as language and you could implement a DocumentMatcher
to check if the module of the file have some Java class in the classpath.

The DocumentMatcher is executed in a non blocking read action.

A document matcher looks like this:

```java
package my.language.server;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.redhat.devtools.lsp4ij.AbstractDocumentMatcher;
import org.jetbrains.annotations.NotNull;

public class MyDocumentMatcher extends AbstractDocumentMatcher {

@Override
public boolean match(@NotNull VirtualFile virtualFile, @NotNull Project project) {
return true;
}
}
```

and it must be registered in language mapping with `documentMatcher` attribute:

```xml
</idea-plugin>

<extensions defaultExtensionNs="com.redhat.devtools.lsp4ij">

<languageMapping language="XML"
serverId="myLanguageServerId"
documentMatcher="my.language.server.MyDocumentMatcher" />

</extensions>
```

## Declare some IJ features

When it is possible, LSP4IJ declare the IJ feature with `any` language
For instance `textDocument/publishDiagnostics` is managed with `externalAnnotator` in LSP4IJ with empty language to support any language:

```xml
<!-- LSP textDocument/publishDiagnostics notification support -->
<externalAnnotator
id="LSPDiagnosticAnnotator"
language=""
implementationClass="com.redhat.devtools.lsp4ij.operations.diagnostics.LSPDiagnosticAnnotator"/>
```

Some IJ feature cannot use any language, so you need to declare it in your plugin:

* `textDocument/hover`:

```xml
<lang.documentationProvider
language="MyLanguage"
implementationClass="com.redhat.devtools.lsp4ij.operations.documentation.LSPDocumentationProvider"
order="first"/>
```

* `textDocument/codeLens`:

```xml
<codeInsight.inlayProvider
language="MyLanguage"
implementationClass="com.redhat.devtools.lsp4ij.operations.codelens.LSPCodelensInlayProvider"/>
```

* `textDocument/inlayHint`:

```xml
<codeInsight.inlayProvider
language="MyLanguage"
implementationClass="com.redhat.devtools.lsp4ij.operations.codelens.LSPInlayHintInlayProvider"/>
```
72 changes: 72 additions & 0 deletions docs/LSPSupport.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# LSP support

As `LSP4IJ` doesn't implement full specification of LSP, this section list the LSP implementation for IntelliJ

## Text Document Synchronization

Here the `LSP4IJ` support for [Text Document Synchronization](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_synchronization):

### Implemented

* [textDocument/didOpen](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didOpen).
* [textDocument/didChange](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didChange).
* [textDocument/didClose](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didClose).
* [textDocument/didSave](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didSave).

### To implement...

## Language Features

Here the `LSP4IJ` support for [Language Features]( https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#languageFeatures)

### Implemented

#### textDocument/documentHighlight

[textDocument/documentHighlight](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentHighlight) is implemented with
`highlightUsagesHandlerFactory` extension point. As this extension point supports `any` language, it works out of the box.

Here a sample with `Qute language server` which highlights item variables:

![textDocument/documentHighlight](./images/lsp-support/textDocument_documentHighlight.png)

#### textDocument/documentLink

[textDocument/documentLink](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentLink) is implemented with:

* `externalAnnotator` extension point to display documentLink with hyperlink renderer.
* `gotoDeclarationHandler` extension point to open the file of the documentLink.

As those extension points support `any` language, it works out of the box.

Here a sample with `Qute language server` which displays include with hyperlink renderer (Ctrl+Click opens the document link):

![textDocument/documentLink](./images/lsp-support/textDocument_documentLink.png)

#### textDocument/hover

[textDocument/hover](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover) is implemented with `lang.documentationProvider` extension point. As this extension point doesn't support `any` language, you need to declare the support in your `plugin.xml`:

```xml
<lang.documentationProvider
language="MyLanguage"
implementationClass="com.redhat.devtools.lsp4ij.operations.documentation.LSPDocumentationProvider"
order="first"/>
```

Here a sample with `Qute language server` which hover include section to show documentation:

![textDocument/hover](./images/lsp-support/textDocument_hover.png)

### To implement...

* [textDocument/declaration](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_declaration).
* [textDocument/typeDefinition](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_typeDefinition).
* [textDocument/implementation](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_implementation).
* [textDocument/references](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references).
* [textDocument/prepareCallHierarchy](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareCallHierarchy).
* [textDocument/incomingCalls](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_incomingCalls).
* [textDocument/outgoingCalls](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_outgoingCalls).
* [textDocument/prepareTypeHierarchy](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareTypeHierarchy).
* [documentLink/resolve](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentLink_resolve)

26 changes: 26 additions & 0 deletions docs/UserGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# User guide

## LSP console

Although not directly useful to users in most cases, the Language Server console view is extremely important
when we need to troubleshoot issues with the language servers.

The state of the servers is visible, stop and restart is available with a right-click, and you can enable different levels of tracing:

![LSP console](./images/LSPConsoleSettings.png)

The communication details between the IDE and the language servers are seen in the "LSP consoles" pane.
In verbose mode, the messages can be expanded for more details:

![LSP console](./images/LSPConsole.png)

When language server is started, several action like stopping the language server or copy the command which starts the language servers are available:

![LSP console Actions](./images/LSPConsoleActions.png)

## Language Servers preferences

The preference page under `Preferences | Languages & Frameworks | Language Servers` allows power users
to configure language servers debugging and tracing:

![Language Server preferences](./images/LanguageServerPreferences.png)
Binary file added docs/images/LSPConsoleActions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/LSPConsoleSettings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added docs/images/MyLanguageServerInLSPConsole.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/lsp-support/textDocument_hover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f0dc06b

Please sign in to comment.