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 6b6e01d commit 2b933c6
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 11 deletions.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,21 @@ 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.

![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
255 changes: 255 additions & 0 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
# 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>
```

## Supported LSP features

Here the current supported LSP features:

| LSP support | Implemented with | Action required |
|---------------------------------|--------------------------------------------|-----------------|
| textDocument/publishDiagnostics | externalAnnotator | No |
| textDocument/completion | completion.contributor | No |
| textDocument/definition | gotoDeclarationHandler | No |
| textDocument/documentLink | externalAnnotator + gotoDeclarationHandler | No |
| textDocument/documentHighlight | highlightUsagesHandlerFactory | No |
| textDocument/hover | lang.documentationProvider | Yes |
| textDocument/codeLens | codeInsight.inlayProvider | Yes |
| textDocument/inlayHint | codeInsight.inlayProvider | Yes |

When it is possible, LSP4IJ declare the IJ feature with language any (see Action required with No).
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 (see Action required with Yes):

* `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"/>
```
12 changes: 12 additions & 0 deletions docs/UserGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# User guide

## LSP console



## Language Servers preferences

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

:
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.
21 changes: 16 additions & 5 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,32 @@
</change-notes>
<description>
<![CDATA[
<p>LSP4IJ is a free and open-source <a href="https://microsoft.github.io/language-server-protocol/">Language Server protocol (LSP)</a> client compatible with all flavors of IntelliJ.</p>
<p><a href="https://github.com/redhat-developer/lsp4ij" >LSP4IJ</a> is a free and open-source <a href="https://microsoft.github.io/language-server-protocol/">Language Server protocol (LSP)</a> client compatible with all flavors of IntelliJ.</p>
<p>It currently doesn't provide any useful functionality on its own but is instead used as a dependency for other extensions, willing to integrate language servers with IntelliJ products.</p>
<h3>LSP4IJ provides:</h3>
<ul>
<li>Language server <code>extension points</code> to add any language server:
<ul>
<li><code>com.redhat.devtools.lsp4ij.server</code> extension point to define a language server.</li>
<li><code>com.redhat.devtools.lsp4ij.languageMapping</code> to associate an IntelliJ language with a language server definition.</li>
<li><a href="https://github.com/redhat-developer/lsp4ij/blob/main/docs/DeveloperGuide.md#declare-server-with-extension-point" >com.redhat.devtools.lsp4ij.server</a> extension point to define a language server.</li>
<li><a href="https://github.com/redhat-developer/lsp4ij/blob/main/docs/DeveloperGuide.md#declare-language-mapping-with-extension-point" >com.redhat.devtools.lsp4ij.languageMapping</a> to associate an IntelliJ language with a language server definition.</li>
</ul>
</li>
<li>An <code>LSP Consoles view</code> to track LSP requests, responses, and notifications in a console.</li>
<li>A <code>Language Servers settings page</code> to configure the LSP trace level and the debug port to use to debug the language server.</li>
<li>An <a href="https://github.com/redhat-developer/lsp4ij/blob/main/docs/UserGuide.md#lsp-console" >LSP Consoles view</a> to track LSP requests, responses, and notifications in a console.</li>
<li>A <a href="https://github.com/redhat-developer/lsp4ij/blob/main/docs/UserGuide.md#language-servers-preferences" >Language Servers preferences page</a> to configure the LSP trace level and the debug port to use to debug the language server.</li>
</ul>
<p>You can start with :
<ul>
<li>
<a href="https://github.com/redhat-developer/lsp4ij/blob/main/docs/DeveloperGuide.md" > which explains step by step how to integrate a language server in LSP4J in an external IntelliJ plugin.
</li>
<li>
<a href="https://github.com/redhat-developer/lsp4ij/blob/main/docs/UserGuide.md" > which explains how to use LSP console and Language server preferences.
</li>
</ul>
</p>
]]>
</description>

Expand Down

0 comments on commit 2b933c6

Please sign in to comment.