Repository: clojure-lsp/clojure-lsp
Pull request #267 (2021-01-24 04:04:19)

GraalVM build
This PR adds `graalvm/native-compile.sh` script to compile clojure-lsp with graalvm

With this PR, the script compiles `clojure-lsp` using a graalvm-ce11-21.0.0
Issues solved:

### To make it work:
- [X] __sqlite__: it needed to declare all reflection classes used by sqlite lib with a [fix](https://github.com/ericdallo/sqlite-jni-graal-fix), I could not make it work using only the `reflection.json`, we could improve that later.
- [x] __log__: I could not make log4j, slf4j, logback or log4j2 to work with graalvm so I changed our lib log to `timbre` which has the same features we use and it's easier to configure, still we need this fix to be merged: https://github.com/ptaoussanis/timbre/pull/334
- [X] __nrepl-server__: graalvm does not work with nrepl-server because of variables [using `future` at top-level](https://github.com/nrepl/nrepl/blob/master/src/clojure/nrepl/util/completion.clj#L170-L180), so I commented it for now, we can use as only dev deps anyway before the merge.
- [X] __dynamic proxy__: I used the graalvm's java with the agent flag to generate these files
- [X] __type-hint__: I needed to change some places to use explicit clojure type-hint since graalvm could not understand what kind of class was that on runtime.

### To allow merge:
- [x] Fix nrepl dep to be local or check for another lib as a replacement.
- [ ] ~Change timbre log for java.util.Logging~ Could not make it work
- [x] Remove all unnecessary classes from reflect.json auto generated.
- [x] setup CI to build for at least Linux
- [x] __test all features__: Since most of errors happen on runtime, we need to test every single feature to make sure no runtime exceptions happen (printed on `/tmp/clojure-lsp.out`)

### Performance:
clojure-lsp can be split into two kind of analysis: startup and after startup since most work is done during the classpath analysis on the project start for the first time, so with my tests this was the ~ result:

Multiple times tested on `NixOS 16 Gb ddr4 RAM, i7-8550U` with a medium/big project of classpath of __250__ files.

| Kind                       | JVM                                | GraalVM              |
|----------------------------|------------------------------------|----------------------|
| Startup TIME*               | ~70s                               | ~91s                 |
| Startup MEM                | max 2.04GB                         | max 1.48GB           |
| Startup CPU                | ~75-85% with max 91%               | ~20-30% with max 43% |
| After startup (cached) CPU | 50% then slowly decreases to ~4-5% | ~1-3%                |
| After startup (cached) MEM | ~736MB                             | ~384MB               |

*Considering kondo classpath scan

Huge thanks to @borkdude which helped me a lot fixing those issues and teaching me ;)


Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1d5784df..0338332d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -26,8 +26,8 @@ jobs:
           tag_name: ${{ steps.latest-tag.outputs.tag}}
           release_name: ${{ steps.latest-tag.outputs.tag}}
 
-  build-binary:
-    name: Build binary
+  build-jvm:
+    name: Build JVM jar and embedded jar
     needs: release
     runs-on: ubuntu-latest
 
@@ -43,30 +43,49 @@ jobs:
         with:
           lein: 2.9.5
 
-      - name: Generate binary
-        run: lein bin
+      - name: Generate jar and embedded jar
+        run: lein with-profiles +native-image bin
 
-      - uses: actions/upload-artifact@v1
+      - name: Get latest tag
+        id: latest-tag
+        uses: WyriHaximus/github-action-get-previous-tag@0.1.0
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Upload jar
+        uses: actions/upload-artifact@v2
+        with:
+          path: target/clojure-lsp-${{ steps.latest-tag.outputs.tag}}-standalone.jar
+          name: clojure-lsp.jar
+
+      - name: Upload embedded jar
+        uses: actions/upload-artifact@v2
         with:
-          name: clojure-lsp
           path: target/clojure-lsp
+          name: clojure-lsp
 
-  upload-binary:
-    name: Upload binary to release
-    needs: build-binary
+  deploy-jvm:
+    name: Deploy JVM jar and embedded jar
+    needs: build-jvm
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v1
-      - uses: actions/download-artifact@v2
-        with:
-          name: clojure-lsp
       - name: Get latest tag
         id: latest-tag
         uses: WyriHaximus/github-action-get-previous-tag@0.1.0
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      - name: Upload linux binary to release
+      - uses: actions/download-artifact@v2
+
+      - name: Upload jar to release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: clojure-lsp.jar
+          asset_name: clojure-lsp.jar
+          tag: ${{ steps.latest-tag.outputs.tag}}
+
+      - name: Upload JVM unix embbeded jar to release
         uses: svenstaro/upload-release-action@v2
         with:
           repo_token: ${{ secrets.GITHUB_TOKEN }}
@@ -74,7 +93,7 @@ jobs:
           asset_name: clojure-lsp
           tag: ${{ steps.latest-tag.outputs.tag}}
 
-      - name: Upload Windows binary to release
+      - name: Upload JVM Windows embedded jar to release
         uses: svenstaro/upload-release-action@v2
         with:
           repo_token: ${{ secrets.GITHUB_TOKEN }}
@@ -82,56 +101,43 @@ jobs:
           asset_name: clojure-lsp.bat
           tag: ${{ steps.latest-tag.outputs.tag}}
 
-  build-jar:
-    name: Build jar
-    needs: release
+  deploy-native-linux:
+    name: Deploy native linux binary
+    needs: build-jvm
     runs-on: ubuntu-latest
 
     steps:
       - uses: actions/checkout@v1
-      - name: Prepare java
-        uses: actions/setup-java@v1
-        with:
-          java-version: 1.8
-
-      - name: Install leiningen
-        uses: DeLaGuardo/setup-clojure@master
-        with:
-          lein: 2.9.5
-
-      - name: Generate jar
-        run: lein uberjar
-
-      - name: Get latest tag
-        id: latest-tag
-        uses: WyriHaximus/github-action-get-previous-tag@0.1.0
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
-      - uses: actions/upload-artifact@v1
+      - uses: actions/download-artifact@v2
         with:
           name: clojure-lsp.jar
-          path: target/clojure-lsp-${{ steps.latest-tag.outputs.tag}}-standalone.jar
-
-  upload-jar:
-    name: Upload jar to release
-    needs: build-jar
-    runs-on: ubuntu-latest
 
-    steps:
-      - uses: actions/checkout@v1
-      - uses: actions/download-artifact@v2
+      - name: Cache GraalVM
+        uses: actions/cache@v1
+        id: cache-graalvm
         with:
-          name: clojure-lsp.jar
-      - name: Get latest tag
-        id: latest-tag
-        uses: WyriHaximus/github-action-get-previous-tag@0.1.0
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      - name: Upload jar to release
-        uses: svenstaro/upload-release-action@v2
+          path: ~/graalvm-ce-java11-21.0.0
+          key: ${{ runner.os }}-graalvm-21.0.0
+          restore-keys: |
+                ${{ runner.os }}-graalvm-21.0.0
+
+      - name: Download GraalVM
+        run: |
+          cd ~
+          if ! [ -d graalvm-ce-java11-21.0.0 ]; then
+            curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-amd64-21.0.0.tar.gz
+            tar xzf graalvm-ce-java11-linux-amd64-21.0.0.tar.gz
+          fi
+
+      - name: Build Linux native image
+        run: |
+          export CLOJURE_LSP_JAR=clojure-lsp.jar
+          export CLOJURE_LSP_XMX="-J-Xmx6g"
+          export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0"
+          graalvm/native-compile.sh
+
+      - name: Upload artifact
+        uses: actions/upload-artifact@v2
         with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: clojure-lsp-${{ steps.latest-tag.outputs.tag}}-standalone.jar
-          asset_name: clojure-lsp.jar
-          tag: ${{ steps.latest-tag.outputs.tag}}
+          path: clojure-lsp
+          name: clojure-lsp-${{ steps.latest-tag.outputs.tag}}-linux-amd64.zip
diff --git a/.lsp/config.edn b/.lsp/config.edn
index 255428ce..ff83c6b3 100644
--- a/.lsp/config.edn
+++ b/.lsp/config.edn
@@ -3,6 +3,6 @@
                     and [[:inner 1]]
                     or [[:inner 1]]
                     are [[:inner 0]]}}
- :clj-kondo {:linters {:unused-namespace {:exclude [clojure.tools.logging]}}}
+ :clj-kondo {:linters {:unused-namespace {:exclude [taoensso.timbre]}}}
  :semantic-tokens? true
  :use-metadata-for-privacy? true}
diff --git a/deps.edn b/deps.edn
index 70157575..5a44c6e0 100644
--- a/deps.edn
+++ b/deps.edn
@@ -1,23 +1,25 @@
-{:deps {org.clojure/clojure {:mvn/version "1.10.1"}
+{:deps {org.clojure/clojure {:mvn/version "1.10.2"}
         org.clojure/tools.reader {:mvn/version "1.3.4"}
         org.eclipse.lsp4j/org.eclipse.lsp4j {:mvn/version "0.10.0"  :exclusions [org.eclipse.xtend/org.eclipse.xtend.lib]}
         org.eclipse.xtend/org.eclipse.xtend.lib {:mvn/version "2.25.0.M1" :exclusions [com.google.guava/guava]}
         com.google.guava/guava {:mvn/version "30.1-jre"}
         rewrite-clj/rewrite-clj {:mvn/version "0.6.1"}
         org.clojure/data.json {:mvn/version "1.0.0"}
-        log4j/log4j {:mvn/version "1.2.17" :exclusions [javax.mail/mail
-                                                        javax.jms/jms
-                                                        com.sun.jdmk/jmxtools
-                                                        com.sun.jmx/jmxri]}
-        org.clojure/tools.logging {:mvn/version "1.1.0"}
-        nrepl/nrepl {:mvn/version "0.8.3"}
+        ericdallo/timbre {:mvn/version "5.1.2" :exclusions [org.julienxx/clj-slack
+                                                            com.mchange/c3p0
+                                                            org.graylog2/gelfclient
+                                                            ymilky/franzy
+                                                            com.newrelic.agent.java/newrelic-agent
+                                                            cljs-node-io
+                                                            congomongo
+                                                            raven-clj]}
         org.clojure/core.async {:mvn/version "1.3.610"}
         org.xerial/sqlite-jdbc {:mvn/version "3.34.0"}
         seancorfield/next.jdbc {:mvn/version "1.1.613"}
         digest/digest {:mvn/version "1.4.10"}
+        borkdude/dynaload {:mvn/version "0.2.2"}
         cljfmt/cljfmt {:mvn/version "0.7.0" :exclusions [rewrite-cljs/rewrite-cljs]}
         medley/medley {:mvn/version "1.3.0"}
-        com.taoensso/tufte {:mvn/version "2.2.0"}
         trptcolin/versioneer {:mvn/version "0.2.0"}
         clj-kondo/clj-kondo {:mvn/version "2021.01.21-20210126.140057-6"}}
  :paths ["resources" "src"]
diff --git a/docs/capabilities.md b/docs/capabilities.md
index dbc0d160..3b6b210b 100644
--- a/docs/capabilities.md
+++ b/docs/capabilities.md
@@ -22,7 +22,7 @@ Below are all the currently supported LSP capabilities and their implementation
 | client/unregisterCapability            |      |                                               |
 | workspace/workspaceFolders             |      |                                               |
 | workspace/didChangeWorkspaceFolders    |      |                                               |
-| workspace/didChangeConfiguration       | √    | Currently only log                            |
+| workspace/didChangeConfiguration       | √    | Currently does nothing but log                |
 | workspace/configuration                |      |                                               |
 | workspace/didChangeWatchedFiles        | √    |                                               |
 | workspace/symbol                       | √    |                                               |
diff --git a/docs/features.md b/docs/features.md
new file mode 100644
index 00000000..7b5bcbb3
--- /dev/null
+++ b/docs/features.md
@@ -0,0 +1,43 @@
+# All features
+
+- Find a function/var definition
+- Find all references of a function/var
+- Show all workspace/project symbols
+- Show all symbols on current file
+- Execute command
+  - Refactoring
+    - Add import to namespace
+    - Add missing namespace (available via code actions too)
+    - Extract Function (available via code actions too)
+    - Inline Symbol (available via code actions too)
+    - Clean namespace (available via code actions too)
+    - Cycle privacy of def/defn
+    - Cycle collection ({},[],())
+    - Expand let
+    - Introduce let
+    - Move expression to let
+    - Thread first expression
+    - Thread last expression
+    - Thread first all
+    - Thread last all
+    - Unwind all
+    - Unwind thread
+  - Dev
+    - Server information
+    - Cursor information
+- Rename symbols
+- Cursor hover showing symbol usages
+- Code actions
+  - Clean namespace require/imports
+  - Inline symbol
+  - Extract function
+  - Add missing require
+  - Add missing import
+  - Add known common require
+  - Add known common import
+- Code lenses on function/vars references
+- Format whole file
+- Format a code range
+- Semantic tokens (applying color on client editor for each known token)
+- Call hierarchy (Incoming only)
+- Custom message to client during any server process
diff --git a/graalvm/jni-config.json b/graalvm/jni-config.json
new file mode 100644
index 00000000..1df130f1
--- /dev/null
+++ b/graalvm/jni-config.json
@@ -0,0 +1,13 @@
+[
+{
+  "name":"clojure_lsp.main",
+  "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }]
+},
+{
+  "name":"java.lang.ClassLoader",
+  "methods":[
+    {"name":"getPlatformClassLoader","parameterTypes":[] }, 
+    {"name":"loadClass","parameterTypes":["java.lang.String"] }
+  ]
+}
+]
diff --git a/graalvm/native-compile.sh b/graalvm/native-compile.sh
new file mode 100755
index 00000000..14c3e66d
--- /dev/null
+++ b/graalvm/native-compile.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+set -e
+
+if [ -z "$GRAALVM_HOME" ]; then
+    echo "Please set GRAALVM_HOME"
+    exit 1
+fi
+
+if [[ ! -f "$CLOJURE_LSP_JAR" ]]
+then
+    lein with-profiles +native-image "do" clean, uberjar
+    CLOJURE_LSP_JAR=$(ls target/clojure-lsp-*-standalone.jar)
+fi
+
+CLOJURE_LSP_XMX=${CLOJURE_LSP_XMX:-"-J-Xmx4g"}
+
+"$GRAALVM_HOME/bin/gu" install native-image
+
+args=("-jar" "$CLOJURE_LSP_JAR"
+      "-H:Name=clojure-lsp"
+      "-J-Dclojure.compiler.direct-linking=true"
+      "-J-Dclojure.spec.skip-macros=true"
+      "-H:+ReportExceptionStackTraces"
+      "--enable-url-protocols=jar"
+      "-H:+InlineBeforeAnalysis"
+      "-H:Log=registerResource:"
+      "--verbose"
+      "-H:IncludeResources='db/.*|static/.*|templates/.*|.*.yml|.*.xml|.*/org/sqlite/.*|org/sqlite/.*|.*.properties'"
+      "-H:ConfigurationFileDirectories=graalvm"
+      "--initialize-at-build-time"
+      "--report-unsupported-elements-at-runtime"
+      "--no-server"
+      "--no-fallback"
+      "--native-image-info"
+      "--allow-incomplete-classpath"
+      "-H:ServiceLoaderFeatureExcludeServices=javax.sound.sampled.spi.AudioFileReader"
+      "-H:ServiceLoaderFeatureExcludeServices=javax.sound.midi.spi.MidiFileReader"
+      "-H:ServiceLoaderFeatureExcludeServices=javax.sound.sampled.spi.MixerProvider"
+      "-H:ServiceLoaderFeatureExcludeServices=javax.sound.sampled.spi.FormatConversionProvider"
+      "-H:ServiceLoaderFeatureExcludeServices=javax.sound.sampled.spi.AudioFileWriter"
+      "-H:ServiceLoaderFeatureExcludeServices=javax.sound.midi.spi.MidiDeviceProvider"
+      "-H:ServiceLoaderFeatureExcludeServices=javax.sound.midi.spi.SoundbankReader"
+      "-H:ServiceLoaderFeatureExcludeServices=javax.sound.midi.spi.MidiFileWriter"
+      "$CLOJURE_LSP_XMX")
+
+"$GRAALVM_HOME/bin/native-image" "${args[@]}"
diff --git a/graalvm/proxy-config.json b/graalvm/proxy-config.json
new file mode 100644
index 00000000..f9c6432f
--- /dev/null
+++ b/graalvm/proxy-config.json
@@ -0,0 +1,3 @@
+[
+  ["org.eclipse.lsp4j.services.LanguageClient","org.eclipse.lsp4j.jsonrpc.Endpoint"]
+]
diff --git a/graalvm/reflect-config.json b/graalvm/reflect-config.json
new file mode 100644
index 00000000..5f0492f5
--- /dev/null
+++ b/graalvm/reflect-config.json
@@ -0,0 +1,1019 @@
+[
+{
+  "name":"clojure.lang.PersistentArrayMap",
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"clojure.lang.PersistentVector",
+  "allDeclaredMethods":true,
+  "allPublicMethods":true
+},
+{
+  "name":"clojure.lang.Symbol",
+  "allPublicMethods":true
+},
+{
+  "name":"clojure.lang.Var",
+  "allPublicMethods":true
+},
+{
+  "name":"clojure_lsp.ClojureExtensions",
+  "allDeclaredMethods":true
+},
+{
+  "name":"clojure_lsp.main.LSPTextDocumentService",
+  "allDeclaredMethods":true
+},
+{
+  "name":"clojure_lsp.main.LSPWorkspaceService",
+  "allDeclaredMethods":true
+},
+{
+  "name":"com.google.gson.JsonObject",
+  "allPublicMethods":true
+},
+{
+  "name":"com.google.gson.JsonPrimitive",
+  "allPublicMethods":true
+},
+{
+  "name":"java.lang.Class",
+  "allPublicMethods":true
+},
+{
+  "name":"java.lang.Object",
+  "allDeclaredFields":true,
+  "allDeclaredMethods":true,
+  "allPublicMethods":true,
+  "allDeclaredConstructors":true
+},
+{
+  "name":"java.lang.String",
+  "allPublicMethods":true
+},
+{
+  "name":"java.util.ArrayList",
+  "allPublicConstructors":true
+},
+{
+  "name":"java.util.Properties",
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.AbstractWorkDoneProgressOptions",
+  "allDeclaredFields":true
+},
+{
+  "name":"org.eclipse.lsp4j.ApplyWorkspaceEditParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ApplyWorkspaceEditResponse",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CallHierarchyCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CallHierarchyIncomingCallsParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CallHierarchyIncomingCall",
+  "allDeclaredFields":true,
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.CallHierarchyItem",
+  "allDeclaredFields":true,
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.CallHierarchyPrepareParams",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ClientCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ClientInfo",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeAction",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionContext",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionDisabled",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionKind",
+  "allDeclaredFields":true,
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionKindCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionLiteralSupportCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeActionResolveSupportCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeLens",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeLensCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeLensOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CodeLensParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ColorProviderCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ColorProviderOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.Command",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionItem",
+  "allDeclaredFields":true,
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionItemCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionItemKind",
+  "fields":[{"name":"value"}]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionItemKindCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionItemResolveSupportCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionItemTag",
+  "fields":[{"name":"value"}]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionItemTagSupportCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CompletionParams",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CreateFile",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.CreateFileOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DeclarationCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DefinitionCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DefinitionParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DeleteFile",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DeleteFileOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.Diagnostic",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DiagnosticCodeDescription",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DiagnosticRelatedInformation",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DiagnosticSeverity",
+  "allPublicMethods":true,
+  "fields":[{"name":"value"}]
+},
+{
+  "name":"org.eclipse.lsp4j.DiagnosticTag",
+  "fields":[{"name":"value"}]
+},
+{
+  "name":"org.eclipse.lsp4j.DiagnosticsTagSupport",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidChangeConfigurationCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidChangeConfigurationParams",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidChangeTextDocumentParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidChangeWatchedFilesCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidChangeWatchedFilesParams",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidChangeWatchedFilesRegistrationOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidCloseTextDocumentParams",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidOpenTextDocumentParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DidSaveTextDocumentParams",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentFilter",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentFormattingParams",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentHighlight",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentHighlightCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentHighlightKind",
+  "fields":[{"name":"value"}]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentHighlightParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentLinkCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentLinkOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.FormattingOptions",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentOnTypeFormattingOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentFormattingParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentRangeFormattingParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentSymbol",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentSymbolCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DocumentSymbolParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.DynamicRegistrationCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.ExecuteCommandCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ExecuteCommandOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ExecuteCommandParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.FileChangeType",
+  "allPublicMethods":true,
+  "fields":[{"name":"value"}]
+},
+{
+  "name":"org.eclipse.lsp4j.FileEvent",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.FileSystemWatcher",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.FoldingRangeCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.FoldingRangeProviderOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.FormattingCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.Hover",
+  "allDeclaredConstructors":true,
+  "allPublicConstructors":true
+},
+{
+  "name":"org.eclipse.lsp4j.HoverCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.HoverParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ImplementationCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.InitializeParams",
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.InitializeResult",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.InitializedParams",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.Location",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.MarkedString",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.MarkupContent",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.MessageParams",
+  "allDeclaredFields":true,
+  "allDeclaredMethods":true,
+  "allPublicMethods":true,
+  "allDeclaredConstructors":true
+},
+{
+  "name":"org.eclipse.lsp4j.MessageType",
+  "allDeclaredFields":true,
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.OnTypeFormattingCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ParameterInformation"
+},
+{
+  "name":"org.eclipse.lsp4j.ParameterInformationCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.Position",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.PublishDiagnosticsCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.PublishDiagnosticsParams",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.Range",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.RangeFormattingCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ReferenceContext",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ReferenceParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ReferencesCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.Registration",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.RegistrationParams",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.RenameCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.RenameFile",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.RenameFileOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.RenameOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.RenameParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ResourceChange",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ResourceOperation",
+  "allDeclaredFields":true
+},
+{
+  "name":"org.eclipse.lsp4j.SaveOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SelectionRangeCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticHighlightingCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticHighlightingServerCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokens",
+  "allDeclaredFields":true
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokensCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "allPublicConstructors":true
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokensClientCapabilitiesRequests",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokensClientCapabilitiesRequestsFull",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokensLegend",
+  "allDeclaredFields":true
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokensParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "allDeclaredConstructors":true,
+  "allPublicConstructors":true
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokensRangeParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "allDeclaredConstructors":true,
+  "allPublicConstructors":true
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokensServerFull",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ServerCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.ServerInfo",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SignatureHelpCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SignatureHelpOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SignatureHelpParams",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SignatureInformationCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.StaticRegistrationOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SymbolCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SymbolInformation",
+  "allDeclaredFields":true,
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.SymbolKind",
+  "allPublicMethods":true,
+  "fields":[{"name":"value"}]
+},
+{
+  "name":"org.eclipse.lsp4j.SymbolKindCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SymbolTag",
+  "fields":[{"name":"value"}]
+},
+{
+  "name":"org.eclipse.lsp4j.SymbolTagSupportCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.SynchronizationCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentClientCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentContentChangeEvent",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentEdit",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentIdentifier",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentItem",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentPositionAndWorkDoneProgressAndPartialResultParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentPositionAndWorkDoneProgressParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentPositionParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentRegistrationOptions",
+  "allDeclaredFields":true
+},
+{
+  "name":"org.eclipse.lsp4j.TextDocumentSyncOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TextEdit",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TypeDefinitionCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.TypeHierarchyCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.WindowClientCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.WorkDoneProgressAndPartialResultParams",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.WorkspaceClientCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.WorkspaceEdit",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "allDeclaredConstructors":true,
+  "allPublicConstructors":true
+},
+{
+  "name":"org.eclipse.lsp4j.WorkspaceEditCapabilities",
+  "allDeclaredFields":true,
+  "allPublicMethods":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.WorkspaceFoldersOptions",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.WorkspaceServerCapabilities",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.WorkspaceSymbolParams",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.CodeActionResponseAdapter",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.DocumentChangeListAdapter",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.DocumentSymbolResponseAdapter",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.HoverTypeAdapter$Factory",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.InitializeParamsTypeAdapter$Factory",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.LocationLinkListAdapter",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.PrepareRenameResponseAdapter",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.ResourceChangeListAdapter",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.ResourceOperationTypeAdapter",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.SemanticTokensFullDeltaResponseAdapter",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.SymbolInformationTypeAdapter$Factory",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.adapters.VersionedTextDocumentIdentifierTypeAdapter$Factory",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.jsonrpc.json.adapters.JsonElementTypeAdapter$Factory",
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.jsonrpc.messages.CancelParams",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.jsonrpc.messages.Either",
+  "allPublicMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.jsonrpc.messages.ResponseError",
+  "allDeclaredFields":true,
+  "methods":[{"name":"<init>","parameterTypes":[] }]
+},
+{
+  "name":"org.eclipse.lsp4j.services.LanguageClient",
+  "allDeclaredMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.services.LanguageServer",
+  "allDeclaredMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.services.TextDocumentService",
+  "allDeclaredMethods":true
+},
+{
+  "name":"org.eclipse.lsp4j.services.WorkspaceService",
+  "allDeclaredMethods":true
+}
+]
diff --git a/graalvm/resource-config.json b/graalvm/resource-config.json
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/graalvm/resource-config.json
@@ -0,0 +1,1 @@
+{}
diff --git a/graalvm/run-tracing-config.sh b/graalvm/run-tracing-config.sh
new file mode 100755
index 00000000..9b36c55a
--- /dev/null
+++ b/graalvm/run-tracing-config.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+lein "do" clean, uberjar
+GRAAL_VM_DOCKER_IMAGE=springci/graalvm-ce:20.2-dev-java11
+
+jar=$(ls target/clojure-lsp-*-standalone.jar)
+
+outfile="/clojure-lsp/$jar"
+
+args=(
+    "-agentlib:native-image-agent=config-merge-dir=/clojure-lsp/graalvm"
+    "-jar" "$outfile"
+)
+
+docker run -i --rm -v ${PWD}:/clojure-lsp $GRAAL_VM_DOCKER_IMAGE java "${args[@]}"
diff --git a/project.clj b/project.clj
index 91399383..385d2d7a 100644
--- a/project.clj
+++ b/project.clj
@@ -25,9 +25,15 @@
                   tag)}
   :profiles {:dev {:plugins [[com.jakemccrary/lein-test-refresh "0.24.1"]
                              [lein-binplus "0.6.6"]]
-                   :bin {:name "clojure-lsp"}}
+                   :bin {:name "clojure-lsp"}
+                   :dependencies [[nrepl/nrepl "0.8.3"]]}
              :test {:test-selectors {:focused :focused}
                     :dependencies [[pjstadig/humane-test-output "0.9.0"]]
                     :injections [(require 'pjstadig.humane-test-output)
                                  (pjstadig.humane-test-output/activate!)]}
-             :uberjar {:aot :all}})
+             :native-image {:dependencies [[ericdallo/sqlite-jni-graal-fix "0.0.2-graalvm-21.0.0"]]}
+             :uberjar {:aot :all
+                       :jvm-opts ["-Xmx2g"
+                                  "-server"
+                                  "-Dclojure.compiler.direct-linking=true"
+                                  "-Dclojure.spec.skip-macros=true"]}})
diff --git a/resources/log4j.properties b/resources/log4j.properties
deleted file mode 100644
index 83c7c469..00000000
--- a/resources/log4j.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-log4j.rootLogger=DEBUG, FILE
-log4j.appender.FILE=org.apache.log4j.FileAppender
-
-# Set the name of the file
-log4j.appender.FILE.File=/tmp/clojure-lsp.out
-log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
-log4j.appender.FILE.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss} %X{PID} %-5p %c %m%n
diff --git a/src/clojure_lsp/crawler.clj b/src/clojure_lsp/crawler.clj
index 42515f93..fc273b8c 100644
--- a/src/clojure_lsp/crawler.clj
+++ b/src/clojure_lsp/crawler.clj
@@ -10,12 +10,14 @@
    [clojure.java.shell :as shell]
    [clojure.set :as set]
    [clojure.string :as string]
-   [clojure.tools.logging :as log]
+   [taoensso.timbre :as log]
    [digest :as digest])
   (:import
    [java.nio.file Paths]))
 
-(defn ^:private to-file [path child]
+(defn ^:private to-file ^java.io.File
+  [^java.nio.file.Path path
+   ^String child]
   (.toFile (.resolve path child)))
 
 (defn ^:private lookup-classpath [root-path {:keys [classpath-cmd env]}]
@@ -198,11 +200,11 @@
     nil))
 
 (defn ^:private find-raw-project-settings [project-root]
-  (let [config-path (Paths/get ".lsp" (into-array ["config.edn"]))]
+  (let [config-path (.toString (Paths/get ".lsp" (into-array ["config.edn"])))]
     (loop [dir (shared/uri->path project-root)]
       (let [full-config-path (.resolve dir config-path)
             file (.toFile full-config-path)
-            parent-dir (.getParent dir)]
+            parent-dir (.getParent ^java.nio.file.Path dir)]
         (cond
           (.exists file)
           (slurp file)
diff --git a/src/clojure_lsp/db.clj b/src/clojure_lsp/db.clj
index e4489db0..bde48fdf 100644
--- a/src/clojure_lsp/db.clj
+++ b/src/clojure_lsp/db.clj
@@ -1,11 +1,11 @@
 (ns clojure-lsp.db
   (:require
+   [clojure.core.async :as async]
    [clojure.edn :as edn]
-   [clojure.tools.logging :as log]
    [clojure.java.io :as io]
    [next.jdbc :as jdbc]
    [next.jdbc.result-set :as rs]
-   [clojure.core.async :as async]))
+   [taoensso.timbre :as log]))
 
 (defonce db (atom {:documents {}}))
 (defonce diagnostics-chan (async/chan 1))
@@ -13,7 +13,7 @@
 
 (def version 1)
 
-(defn make-spec [project-root]
+(defn ^:private make-spec [project-root]
   (let [lsp-db (io/file (str project-root) ".lsp" "sqlite.db")]
     {:dbtype "sqlite"
      :dbname (.getAbsolutePath lsp-db)}))
diff --git a/src/clojure_lsp/feature/call_hierarchy.clj b/src/clojure_lsp/feature/call_hierarchy.clj
index 2329bde3..b05d785b 100644
--- a/src/clojure_lsp/feature/call_hierarchy.clj
+++ b/src/clojure_lsp/feature/call_hierarchy.clj
@@ -6,7 +6,7 @@
    [clojure-lsp.refactor.edit :as edit]
    [clojure-lsp.shared :as shared]
    [rewrite-clj.zip :as z]
-   [clojure.tools.logging :as log]
+   [taoensso.timbre :as log]
    [clojure-lsp.queries :as q]))
 
 (defn ^:private element-by-uri->call-hierarchy-item
diff --git a/src/clojure_lsp/feature/code_actions.clj b/src/clojure_lsp/feature/code_actions.clj
index 107ea6a1..4253d90e 100644
--- a/src/clojure_lsp/feature/code_actions.clj
+++ b/src/clojure_lsp/feature/code_actions.clj
@@ -6,7 +6,7 @@
     [clojure-lsp.queries :as q]
     [clojure-lsp.db :as db]
     [clojure-lsp.shared :as shared]
-    [clojure.tools.logging :as log])
+    [taoensso.timbre :as log])
   (:import
     (org.eclipse.lsp4j
       CodeActionKind)))
diff --git a/src/clojure_lsp/feature/completion.clj b/src/clojure_lsp/feature/completion.clj
index 230ed3b3..60e66b5f 100644
--- a/src/clojure_lsp/feature/completion.clj
+++ b/src/clojure_lsp/feature/completion.clj
@@ -7,9 +7,9 @@
     [clojure-lsp.queries :as q]
     [clojure-lsp.shared :as shared]
     [clojure.string :as string]
-    [clojure.tools.logging :as log]
     [clojure.walk :as walk]
-    [rewrite-clj.zip :as z]))
+    [rewrite-clj.zip :as z]
+    [taoensso.timbre :as log]))
 
 (defn ^:private remove-keys [pred m]
   (apply dissoc m (filter pred (keys m))))
@@ -183,4 +183,4 @@
           (assoc :documentation (f.hover/hover-documentation data))
           (dissoc :data))
       item)
-    (assoc :kind (some-> kind .toLowerCase keyword))))
+    (assoc :kind (some-> kind str string/lower-case keyword))))
diff --git a/src/clojure_lsp/feature/diagnostics.clj b/src/clojure_lsp/feature/diagnostics.clj
index 56d8623b..2833dd61 100644
--- a/src/clojure_lsp/feature/diagnostics.clj
+++ b/src/clojure_lsp/feature/diagnostics.clj
@@ -3,7 +3,7 @@
     [clojure-lsp.shared :as shared]
     [clojure-lsp.db :as db]
     [clojure.core.async :as async]
-    [clojure.tools.logging :as log]))
+    [taoensso.timbre :as log]))
 
 (defn ^:private kondo-finding->diagnostic [{:keys [type message level row col] :as finding}]
   (let [expression? (not= row (:end-row finding))
diff --git a/src/clojure_lsp/feature/document_symbol.clj b/src/clojure_lsp/feature/document_symbol.clj
index 1d67253d..02a9d468 100644
--- a/src/clojure_lsp/feature/document_symbol.clj
+++ b/src/clojure_lsp/feature/document_symbol.clj
@@ -1,6 +1,6 @@
 (ns clojure-lsp.feature.document-symbol
   (:require
-    [clojure.tools.logging :as log]))
+    [taoensso.timbre :as log]))
 
 (defn declaration? [e]
   (#{:namespace-definitions :var-definitions} (:bucket e)))
diff --git a/src/clojure_lsp/feature/refactor.clj b/src/clojure_lsp/feature/refactor.clj
index cebf0e6b..c986f52b 100644
--- a/src/clojure_lsp/feature/refactor.clj
+++ b/src/clojure_lsp/feature/refactor.clj
@@ -3,7 +3,7 @@
    [clojure-lsp.db :as db]
    [clojure-lsp.refactor.transform :as r.transform]
    [clojure-lsp.shared :as shared]
-   [clojure.tools.logging :as log]
+   [taoensso.timbre :as log]
    [rewrite-clj.zip :as z]))
 
 (defn client-changes [changes]
diff --git a/src/clojure_lsp/feature/semantic_tokens.clj b/src/clojure_lsp/feature/semantic_tokens.clj
index d36d42ae..7e9378d4 100644
--- a/src/clojure_lsp/feature/semantic_tokens.clj
+++ b/src/clojure_lsp/feature/semantic_tokens.clj
@@ -1,7 +1,10 @@
 (ns clojure-lsp.feature.semantic-tokens
   (:require
     [clojure-lsp.db :as db]
-    [clojure-lsp.shared :as shared]))
+    [clojure.string :as string]
+    [clojure-lsp.shared :as shared])
+  (:import
+   [clojure.lang PersistentVector]))
 
 (def token-types
   [:type
@@ -30,7 +33,7 @@
   [(dec name-row)
    (dec name-col)
    (- name-end-col name-col)
-   (.indexOf token-types token-type)
+   (.indexOf ^PersistentVector token-types token-type)
    token-modifier])
 
 (defn ^:private elements->absolute-tokens
diff --git a/src/clojure_lsp/handlers.clj b/src/clojure_lsp/handlers.clj
index 3ba84aa1..151865f5 100644
--- a/src/clojure_lsp/handlers.clj
+++ b/src/clojure_lsp/handlers.clj
@@ -15,12 +15,12 @@
     [clojure-lsp.parser :as parser]
     [clojure-lsp.producer :as producer]
     [clojure-lsp.queries :as q]
+    [taoensso.timbre :as log]
     [clojure-lsp.shared :as shared]
     [clojure.core.async :as async]
     [clojure.pprint :as pprint]
     [clojure.set :as set]
     [clojure.string :as string]
-    [clojure.tools.logging :as log]
     [rewrite-clj.node :as n]
     [rewrite-clj.zip :as z]
     [trptcolin.versioneer.core :as version])
@@ -59,7 +59,7 @@
            "."
            (name file-type)))))
 
-(defn did-open [{:keys [textDocument]}]
+(defn did-open [{:keys [textDocument] :as params}]
   (let [uri (-> textDocument :uri URLDecoder/decode)
         text (:text textDocument)]
     (when-let [new-ns (and (string/blank? text)
diff --git a/src/clojure_lsp/interop.clj b/src/clojure_lsp/interop.clj
index 932929f3..35bbb5bb 100644
--- a/src/clojure_lsp/interop.clj
+++ b/src/clojure_lsp/interop.clj
@@ -4,7 +4,7 @@
     [clojure.java.data :as j]
     [clojure.spec.alpha :as s]
     [clojure.string :as string]
-    [clojure.tools.logging :as log]
+    [taoensso.timbre :as log]
     [clojure.walk :as walk]
     [medley.core :as medley])
   (:import
@@ -22,6 +22,7 @@
       DiagnosticSeverity
       DocumentHighlight
       DocumentSymbol
+      FileChangeType
       Hover
       Location
       MarkedString
@@ -44,32 +45,33 @@
     (org.eclipse.lsp4j.jsonrpc.messages Either)
     (java.net URLDecoder)))
 
-(defn ^:private enum->keyword [enum]
-  (-> enum (.name) .toLowerCase keyword))
-
-(defn learn-how-to-parse-enums [enums]
-  (-> #(defmethod j/from-java % [instance] (enum->keyword instance))
-      (map enums)))
-
-(defn document->decoded-uri [document]
+(defn document->decoded-uri [^TextDocumentIdentifier document]
   (-> document
       .getUri
       URLDecoder/decode))
 
-(learn-how-to-parse-enums
-  [DiagnosticSeverity
-   MessageType
-   WatchKind
-   CompletionItemKind
-   SymbolKind])
+(defmethod j/from-java DiagnosticSeverity [^DiagnosticSeverity instance]
+  (-> instance .name .toLowerCase keyword))
+
+(defmethod j/from-java MessageType [^MessageType instance]
+  (-> instance .name .toLowerCase keyword))
+
+(defmethod j/from-java WatchKind [^WatchKind instance]
+  (-> instance .name .toLowerCase keyword))
+
+(defmethod j/from-java CompletionItemKind [^CompletionItemKind instance]
+  (-> instance .name .toLowerCase keyword))
 
-(defmethod j/from-java Either [instance]
+(defmethod j/from-java SymbolKind [^SymbolKind instance]
+  (-> instance .name .toLowerCase keyword))
+
+(defmethod j/from-java Either [^Either instance]
   (j/from-java (.get instance)))
 
-(defmethod j/from-java TextDocumentIdentifier [instance]
+(defmethod j/from-java TextDocumentIdentifier [^TextDocumentIdentifier instance]
   (document->decoded-uri instance))
 
-(defmethod j/from-java JsonElement [instance]
+(defmethod j/from-java JsonElement [^JsonElement instance]
   (-> instance
       .toString
       json/read-str
@@ -306,7 +308,7 @@
 
 (s/def ::call-hierarchy-incoming-calls (s/coll-of ::call-hierarchy-incoming-call))
 
-(defn  java->clj [inst]
+(defn java->clj [inst]
   (let [converted (j/from-java inst)]
     (if (map? converted)
       (->> converted
@@ -327,8 +329,7 @@
                              (keyword map-key))))))
 
 (s/def ::debean (s/conformer debeaner))
-(s/def ::value-set (s/conformer (fn [value-set]
-                                  (set (map #(.getValue %) value-set)))))
+
 (s/def :capabilities/code-action ::debean)
 (s/def :capabilities/code-lens ::debean)
 (s/def :capabilities/color-provider ::debean)
@@ -347,15 +348,26 @@
 (s/def :capabilities/synchronization ::debean)
 (s/def :capabilities/type-definition ::debean)
 
+
+(s/def :capabilities/symbol-kind-value-set
+  (s/conformer (fn [value-set]
+                 (set (map (fn [^SymbolKind kind]
+                             (.getValue kind)) value-set)))))
+
 (s/def :capabilities/symbol-kind (s/and ::debean
-                                        (s/keys :opt-un [::value-set])))
+                                        (s/keys :opt-un [:capabilities/symbol-kind-value-set])))
 (s/def :capabilities/document-symbol (s/and ::debean
                                             (s/keys :opt-un [:capabilities/symbol-kind])))
 (s/def :capabilities/signature-help (s/and ::debean
                                            (s/keys :opt-un [:capabilities/signature-information])))
 
+(s/def :capabilities/completion-item-kind-value-set
+  (s/conformer (fn [value-set]
+                 (set (map (fn [^CompletionItemKind kind]
+                             (.getValue kind)) value-set)))))
+
 (s/def :capabilities/completion-item-kind (s/and ::debean
-                                                 (s/keys :opt-un [::value-set])))
+                                                 (s/keys :opt-un [:capabilities/completion-item-kind-value-set])))
 (s/def :capabilities/completion (s/and ::debean
                                        (s/keys :opt-un [:capabilities/completion-item
                                                         :capabilities/completion-item-kind])))
@@ -398,7 +410,7 @@
                                     (s/keys :opt-un [:capabilities/workspace :capabilities/text-document])))
 
 (def watched-files-type-enum {1 :created 2 :changed 3 :deleted})
-(s/def :watched-files/type (s/conformer (fn [v] (get watched-files-type-enum (.getValue v)))))
+(s/def :watched-files/type (s/conformer (fn [^FileChangeType v] (get watched-files-type-enum (.getValue v)))))
 (s/def :watched-files/change (s/and ::debean (s/keys :req-un [::uri :watched-files/type])))
 (s/def ::watched-files-changes (s/and (s/conformer (fn [vs] (into [] vs)))
                                       (s/coll-of :watched-files/change)))
diff --git a/src/clojure_lsp/main.clj b/src/clojure_lsp/main.clj
index 1ecdeca0..db401348 100644
--- a/src/clojure_lsp/main.clj
+++ b/src/clojure_lsp/main.clj
@@ -5,19 +5,17 @@
     [clojure-lsp.feature.semantic-tokens :as semantic-tokens]
     [clojure-lsp.handlers :as handlers]
     [clojure-lsp.interop :as interop]
+    [clojure-lsp.nrepl :as nrepl]
     [clojure-lsp.producer :as producer]
     [clojure-lsp.shared :as shared]
     [clojure.core.async :as async]
-    [clojure.string :as string]
-    [clojure.tools.logging :as log]
-    [nrepl.server :as nrepl.server]
-    [trptcolin.versioneer.core :as version])
+    [taoensso.timbre :as log]
+    [trptcolin.versioneer.core :as version]
+    borkdude.dynaload)
   (:import
     (clojure_lsp ClojureExtensions)
-    (java.lang.management ManagementFactory)
     (java.util.concurrent CompletableFuture)
     (java.util.function Supplier)
-    (org.apache.log4j MDC)
     (org.eclipse.lsp4j
       CallHierarchyIncomingCallsParams
       CallHierarchyPrepareParams
@@ -71,6 +69,11 @@
     (org.eclipse.lsp4j.services LanguageServer TextDocumentService WorkspaceService LanguageClient))
   (:gen-class))
 
+(log/merge-config! {:appenders {:println {:enabled? false}
+                                :spit (log/spit-appender {:fname "/tmp/clojure-lsp.out"})}})
+
+(log/handle-uncaught-jvm-exceptions!)
+
 (defonce formatting (atom false))
 
 (defonce status (atom {}))
@@ -119,8 +122,8 @@
 (deftype LSPTextDocumentService []
   TextDocumentService
   (^void didOpen [_ ^DidOpenTextDocumentParams params]
-    (go :didOpen
-        (sync-handler params handlers/did-open)))
+   (go :didOpen
+       (sync-handler params handlers/did-open)))
 
   (^void didChange [_ ^DidChangeTextDocumentParams params]
     (go :didChange
@@ -170,8 +173,8 @@
                             0 0)))))
 
   (^CompletableFuture formatting [_ ^DocumentFormattingParams params]
-    (go :formatting
-        (async-handler params handlers/formatting ::interop/edits)))
+   (go :formatting
+       (async-handler params handlers/formatting ::interop/edits)))
 
   (^CompletableFuture rangeFormatting [_this ^DocumentRangeFormattingParams params]
     (go :rangeFormatting
@@ -200,8 +203,8 @@
         (async-handler params handlers/code-actions ::interop/code-actions)))
 
   (^CompletableFuture resolveCodeAction [_ ^CodeAction unresolved]
-    (go :resolveCodeAction
-        (async-handler unresolved handlers/resolve-code-action ::interop/code-action)))
+   (go :resolveCodeAction
+       (async-handler unresolved handlers/resolve-code-action ::interop/code-action)))
 
   (^CompletableFuture codeLens [_ ^CodeLensParams params]
     (go :codeLens
@@ -241,7 +244,6 @@
 
 (deftype LSPWorkspaceService []
   WorkspaceService
-
   (^CompletableFuture executeCommand [_ ^ExecuteCommandParams params]
     (go :executeCommand
         (future
@@ -261,8 +263,8 @@
 
   ;; TODO wait for lsp4j release
   #_(^void didDeleteFiles [_ ^DeleteFilesParams params]
-    (go :didSave
-        (sync-handler params handlers/did-delete-files)))
+                          (go :didSave
+                              (sync-handler params handlers/did-delete-files)))
 
   (^CompletableFuture symbol [_ ^WorkspaceSymbolParams params]
     (go :workspaceSymbol
@@ -270,15 +272,15 @@
 
 (defn client-settings [^InitializeParams params]
   (-> params
-      (.getInitializationOptions)
-      (interop/json->clj)
+      interop/java->clj
+      :initializationOptions
       (or {})
       shared/keywordize-first-depth
       (interop/clean-client-settings)))
 
 (defn client-capabilities [^InitializeParams params]
   (some->> params
-           (.getCapabilities)
+           .getCapabilities
            (interop/conform-or-log ::interop/client-capabilities)))
 
 ;; Called from java
@@ -343,14 +345,13 @@
                                        (.setCompletionProvider (CompletionOptions. true []))))))))))
 
     (^void initialized [^InitializedParams params]
-      (log/info "Initialized" params)
+      (log/info "Initialized")
       (go :initialized
           (end
-            (doto
-             (:client @db/db)
-              (.registerCapability
-                (RegistrationParams. [(Registration. "id" "workspace/didChangeWatchedFiles"
-                                                     (DidChangeWatchedFilesRegistrationOptions. [(FileSystemWatcher. "**")]))]))))))
+            (let [client ^LanguageClient (:client @db/db)]
+              (.registerCapability client
+                                   (RegistrationParams. [(Registration. "id" "workspace/didChangeWatchedFiles"
+                                                                        (DidChangeWatchedFilesRegistrationOptions. [(FileSystemWatcher. "**")]))]))))))
     (^CompletableFuture shutdown []
       (log/info "Shutting down")
       (reset! db/db {:documents {}}) ;; TODO confirm this is correct
@@ -397,44 +398,13 @@
           (log/error e "in thread"))))
     os))
 
-(defn- dot-nrepl-port-file
-  []
-  (try
-    (slurp  ".nrepl-port")
-    (catch Exception _)))
-
-(defn- embedded-nrepl-server
-  []
-  (let [repl-server (nrepl.server/start-server)
-        port (:port repl-server)]
-    port))
-
-(defn- repl-port
-  []
-  (or (dot-nrepl-port-file)
-      (embedded-nrepl-server)))
-
-(defn- inject-pid-for-log4j
-  "Inject a PID context value so we can include it in log4j
-  logs."
-  []
-  (let [pid (-> (ManagementFactory/getRuntimeMXBean)
-                (.getName)
-                (string/split #"@")
-                (first))]
-    (MDC/put "PID" pid)))
-
 (defn- run []
-  (inject-pid-for-log4j)
   (log/info "Starting server...")
   (let [is (or System/in (tee-system-in System/in))
         os (or System/out (tee-system-out System/out))
-        launcher (LSPLauncher/createServerLauncher server is os)
-        port (repl-port)]
-    (log/info "====== LSP nrepl server started on port" port)
-    (swap! db/db assoc
-           :client ^LanguageClient (.getRemoteProxy launcher)
-           :port port)
+        launcher (LSPLauncher/createServerLauncher server is os)]
+    (nrepl/setup-nrepl)
+    (swap! db/db assoc :client ^LanguageClient (.getRemoteProxy launcher))
     (async/go
       (loop [edit (async/<! db/edits-chan)]
         (producer/workspace-apply-edit edit)
diff --git a/src/clojure_lsp/nrepl.clj b/src/clojure_lsp/nrepl.clj
new file mode 100644
index 00000000..b1b01e2b
--- /dev/null
+++ b/src/clojure_lsp/nrepl.clj
@@ -0,0 +1,24 @@
+(ns clojure-lsp.nrepl
+  (:require
+   [borkdude.dynaload :refer [dynaload]]
+   [clojure-lsp.db :as db]
+   [taoensso.timbre :as log]))
+
+(def start-server (dynaload 'nrepl.server/start-server))
+
+(defn ^:private find-dot-nrepl-port-file []
+  (try
+    (slurp ".nrepl-port")
+    (catch Exception _)))
+
+(defn ^:private repl-port []
+  (or (find-dot-nrepl-port-file)
+      (:port (start-server))))
+
+(defn setup-nrepl []
+  (try
+    (when-let [port (repl-port)]
+      (log/info "====== LSP nrepl server started on port" port)
+      (swap! db/db assoc :port port))
+    (catch Throwable _
+      (log/debug "nrepl not found, skipping nrepl server start..."))))
diff --git a/src/clojure_lsp/parser.clj b/src/clojure_lsp/parser.clj
index bf7a3219..7775c2d1 100644
--- a/src/clojure_lsp/parser.clj
+++ b/src/clojure_lsp/parser.clj
@@ -3,7 +3,7 @@
     [clojure-lsp.db :as db]
     [clojure-lsp.refactor.edit :as edit]
     [clojure.string :as string]
-    [clojure.tools.logging :as log]
+    [taoensso.timbre :as log]
     [rewrite-clj.node :as n]
     [rewrite-clj.zip :as z]))
 
diff --git a/src/clojure_lsp/producer.clj b/src/clojure_lsp/producer.clj
index f4fd876c..8701464e 100644
--- a/src/clojure_lsp/producer.clj
+++ b/src/clojure_lsp/producer.clj
@@ -2,8 +2,9 @@
   (:require
     [clojure-lsp.db :as db]
     [clojure-lsp.interop :as interop]
-    [clojure.tools.logging :as log])
+    [taoensso.timbre :as log])
   (:import
+   (org.eclipse.lsp4j.services LanguageClient)
    (org.eclipse.lsp4j
      ApplyWorkspaceEditParams)))
 
@@ -12,18 +13,21 @@
    (window-show-message {:message message :type type}))
   ([message-content]
    (log/info message-content)
-   (->> message-content
-        (interop/conform-or-log ::interop/show-message)
-        (.showMessage (:client @db/db)))))
+   (let [client ^LanguageClient (:client @db/db)]
+     (->> message-content
+          (interop/conform-or-log ::interop/show-message)
+          (.showMessage client)))))
 
 (defn workspace-apply-edit [edit]
-  (->> edit
-       (interop/conform-or-log ::interop/workspace-edit)
-       ApplyWorkspaceEditParams.
-       (.applyEdit (:client @db/db))
-       .get))
+  (let [client ^LanguageClient (:client @db/db)]
+    (->> edit
+         (interop/conform-or-log ::interop/workspace-edit)
+         ApplyWorkspaceEditParams.
+         (.applyEdit client)
+         .get)))
 
 (defn publish-diagnostic [diagnostic]
-  (->> diagnostic
-       (interop/conform-or-log ::interop/publish-diagnostics-params)
-       (.publishDiagnostics (:client @db/db))))
+  (let [client ^LanguageClient (:client @db/db)]
+    (->> diagnostic
+         (interop/conform-or-log ::interop/publish-diagnostics-params)
+         (.publishDiagnostics client))))
diff --git a/src/clojure_lsp/queries.clj b/src/clojure_lsp/queries.clj
index 19b36344..55c44206 100644
--- a/src/clojure_lsp/queries.clj
+++ b/src/clojure_lsp/queries.clj
@@ -1,6 +1,6 @@
 (ns clojure-lsp.queries
   (:require
-   [clojure.tools.logging :as log]
+   [taoensso.timbre :as log]
    [medley.core :as medley]))
 
 (defn inside?
diff --git a/src/clojure_lsp/refactor/edit.clj b/src/clojure_lsp/refactor/edit.clj
index 823a87b3..1da50c29 100644
--- a/src/clojure_lsp/refactor/edit.clj
+++ b/src/clojure_lsp/refactor/edit.clj
@@ -1,6 +1,5 @@
 (ns clojure-lsp.refactor.edit
   (:require
-   [clojure.tools.logging :as log]
    [rewrite-clj.custom-zipper.core :as cz]
    [rewrite-clj.node :as n]
    [rewrite-clj.zip :as z]))
diff --git a/src/clojure_lsp/refactor/transform.clj b/src/clojure_lsp/refactor/transform.clj
index a3a574a4..0bc02709 100644
--- a/src/clojure_lsp/refactor/transform.clj
+++ b/src/clojure_lsp/refactor/transform.clj
@@ -8,7 +8,7 @@
     [clojure-lsp.shared :as shared]
     [clojure.set :as set]
     [clojure.string :as string]
-    [clojure.tools.logging :as log]
+    [taoensso.timbre :as log]
     [medley.core :as medley]
     [rewrite-clj.custom-zipper.core :as cz]
     [rewrite-clj.node :as n]
diff --git a/src/clojure_lsp/shared.clj b/src/clojure_lsp/shared.clj
index 2137e2c2..1633a740 100644
--- a/src/clojure_lsp/shared.clj
+++ b/src/clojure_lsp/shared.clj
@@ -3,7 +3,7 @@
     [clojure-lsp.db :as db]
     [clojure.java.shell :as shell]
     [clojure.string :as string]
-    [clojure.tools.logging :as log])
+    [taoensso.timbre :as log])
   (:import
    [java.net URI]
    [java.nio.file Paths]))
@@ -40,7 +40,7 @@
     (string/ends-with? uri ".edn") :edn
     :else :unknown))
 
-(defn uri->path [uri]
+(defn uri->path ^java.nio.file.Path [uri]
   (.toAbsolutePath (Paths/get (URI. uri))))
 
 (defn uri->filename [uri]


Test patch:
diff --git a/test/clojure_lsp/handlers_test.clj b/test/clojure_lsp/handlers_test.clj
index 292a2dcd..3127bc15 100644
--- a/test/clojure_lsp/handlers_test.clj
+++ b/test/clojure_lsp/handlers_test.clj
@@ -6,7 +6,7 @@
     [clojure.core.async :as async]
     [clojure.string :as string]
     [clojure.test :refer [deftest is testing]]
-    [clojure.tools.logging :as log]))
+    [taoensso.timbre :as log]))
 
 (h/reset-db-after-test)
 
diff --git a/test/clojure_lsp/interop_test.clj b/test/clojure_lsp/interop_test.clj
index 02b093d6..700546e6 100644
--- a/test/clojure_lsp/interop_test.clj
+++ b/test/clojure_lsp/interop_test.clj
@@ -3,12 +3,12 @@
     [clojure-lsp.interop :as interop]
     [clojure.test :refer [deftest is]])
   (:import
-    (org.eclipse.lsp4j TextDocumentItem)))
+    (org.eclipse.lsp4j TextDocumentIdentifier)))
 
 (deftest document->decoded-uri
-    (is (= (interop/document->decoded-uri (new TextDocumentItem "" "clojure" 1 ""))  ""))
-    (is (= (interop/document->decoded-uri (new TextDocumentItem "http%3A%2F%2Ffoo%20bar%2F" "clojure" 1 ""))  "http://foo bar/"))
-    (is (= (interop/document->decoded-uri (new TextDocumentItem "http://foo bar/" "clojure" 1 ""))  "http://foo bar/"))
-    (is (= (interop/document->decoded-uri (new TextDocumentItem "jar:file:///Users/clojure-1.9.0.jar!/clojure/string.clj" "clojure" 1 ""))  "jar:file:///Users/clojure-1.9.0.jar!/clojure/string.clj"))
-    (is (= (interop/document->decoded-uri (new TextDocumentItem "zipfile:///something.jar::something/file.cljc" "clojure" 1 ""))  "zipfile:///something.jar::something/file.cljc"))
-    (is (= (interop/document->decoded-uri (new TextDocumentItem "zipfile:///something.jar%3A%3Asomething/file.cljc" "clojure" 1 ""))  "zipfile:///something.jar::something/file.cljc")))
+    (is (= (interop/document->decoded-uri (TextDocumentIdentifier. ""))  ""))
+    (is (= (interop/document->decoded-uri (TextDocumentIdentifier. "http%3A%2F%2Ffoo%20bar%2F"))  "http://foo bar/"))
+    (is (= (interop/document->decoded-uri (TextDocumentIdentifier. "http://foo bar/"))  "http://foo bar/"))
+    (is (= (interop/document->decoded-uri (TextDocumentIdentifier. "jar:file:///Users/clojure-1.9.0.jar!/clojure/string.clj"))  "jar:file:///Users/clojure-1.9.0.jar!/clojure/string.clj"))
+    (is (= (interop/document->decoded-uri (TextDocumentIdentifier. "zipfile:///something.jar::something/file.cljc"))  "zipfile:///something.jar::something/file.cljc"))
+    (is (= (interop/document->decoded-uri (TextDocumentIdentifier. "zipfile:///something.jar%3A%3Asomething/file.cljc"))  "zipfile:///something.jar::something/file.cljc")))
diff --git a/test/clojure_lsp/parser_test.clj b/test/clojure_lsp/parser_test.clj
index de0a9cc9..9c9548f5 100644
--- a/test/clojure_lsp/parser_test.clj
+++ b/test/clojure_lsp/parser_test.clj
@@ -2,7 +2,7 @@
   (:require
     [clojure-lsp.parser :as parser]
     [clojure.test :refer [deftest is]]
-    [clojure.tools.logging :as log]
+    [taoensso.timbre :as log]
     [rewrite-clj.zip :as z]))
 
 (deftest find-loc-at-pos-test
diff --git a/test/clojure_lsp/queries_test.clj b/test/clojure_lsp/queries_test.clj
index 1b6ebd0a..59dea134 100644
--- a/test/clojure_lsp/queries_test.clj
+++ b/test/clojure_lsp/queries_test.clj
@@ -4,7 +4,7 @@
     [clojure-lsp.queries :as q]
     [clojure-lsp.test-helper :as h]
     [clojure.test :refer [deftest is testing]]
-    [clojure.tools.logging :as log]))
+    [taoensso.timbre :as log]))
 
 (deftest find-element-under-cursor []
   (let [code (str "(ns a.b.c (:require [d.e.f :as |f-alias]))\n"
diff --git a/test/clojure_lsp/test_helper.clj b/test/clojure_lsp/test_helper.clj
index 93c50f13..53611db5 100644
--- a/test/clojure_lsp/test_helper.clj
+++ b/test/clojure_lsp/test_helper.clj
@@ -5,7 +5,7 @@
     [clojure.core.async :as async]
     [clojure.pprint :as pprint]
     [clojure.test :refer [is use-fixtures]]
-    [clojure.tools.logging :as log]))
+    [taoensso.timbre :as log]))
 
 (defn reset-db-after-test []
   (use-fixtures