diff --git a/README.md b/README.md index c7fd18bff..16fb14332 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,14 @@ LSP4IJ provides: * `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: -![LSP console](https://github.com/redhat-developer/lsp4ij/blob/HEAD/docs/images/LSPConsole.png?raw=true) +![LSP console](./docs/images/LSPConsole.png?raw=true) * a `Language Servers settings page` to configure the LSP trace level, the debug port to use to debug language server: -![Language Server settings](https://github.com/redhat-developer/lsp4ij/blob/HEAD/docs/images/LanguageServerSettings.png?raw=true) +![Language Server settings](./docs/images/LanguageServerSettings.png?raw=true) + +You can start with [Getting started](./docs/GettingStarted.md) + ## Who is using LSP4IJ? diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md new file mode 100644 index 000000000..1fafef3a1 --- /dev/null +++ b/docs/GettingStarted.md @@ -0,0 +1,157 @@ +# Getting started + +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 + + ... + + com.redhat.devtools.lsp4ij + + ... + +``` + +### 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 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 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 to use your `my.language.server.MyLanguageServer` and `my.language.server.MyLanguageClient`: + +``` + + + + + + + + + + +``` + +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 + +TODO + * declare mapping + * DocumentMatcher + +## Declare IJ features + + * externalAnnotator, completion, etc are declared an language any with LSP4IJ => nothing to do + * IJ inlayHint, hover doesn't support any language, so you need to declare them in you plugin.xml + + diff --git a/docs/images/MyLanguageServerInLSPConsole.png b/docs/images/MyLanguageServerInLSPConsole.png new file mode 100644 index 000000000..7286a5c91 Binary files /dev/null and b/docs/images/MyLanguageServerInLSPConsole.png differ diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index a82acd686..d331b99ce 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -14,7 +14,7 @@ LSP4IJ is a free and open-source Language Server protocol (LSP) client compatible with all flavors of IntelliJ.

+

LSP4IJ is a free and open-source Language Server protocol (LSP) client compatible with all flavors of IntelliJ.

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.

@@ -29,6 +29,8 @@
  • An LSP Consoles view to track LSP requests, responses, and notifications in a console.
  • A Language Servers settings page to configure the LSP trace level and the debug port to use to debug the language server.
  • + +

    You can start with Getting started

    ]]>