Liking cljdoc? Tell your friends :D

clojure-lsp Development

There are several ways of finding and fixing a bug or implementing a new feature:

  • The Clojure Way
  • Create a test for your bug/feature, then implement the code following the test (TDD).
  • Build clojure-lsp using make each time you have made changes, and test it manually in your client. This is the slowest option.

Whichever development path you choose: For final testing, it is good to rebuild the binary with make.

There are two custom LSP methods clojure/serverInfo/log and clojure/cursorInfo/log. They can assist in debugging.

The Clojure Way

With a clojure-lsp + nREPL powered Clojure editor you can modify your editor session's clojure-lsp server using the Clojure REPL.

Here's demo video: https://www.youtube.com/watch?v=4UvT0yqBDw8

These are the steps:

  1. make - to build a clojure-lsp executable that includes cider-nrepl in the jar. This executable will be saved at the root of the project.
  2. Configure your editor to use this clojure-lsp executable
  3. Have your editor restart its clojure-lsp server
  4. Issue the clojure-lsp serverInfo command
  5. Find the port entry in the output
  6. Connect your editors nREPL client to this port
  7. Hack away!

Seeing is believing. An easy way to convince yourself that you can actually change clojure-lsp mid-flight is to:

  1. Modify the server-info function in src/clojure_lsp/handlers.clj
    • Say, you add a :foo :bar entry to the map returned
  2. Evaluate the new server-info function definition
  3. Issue the clojure-lsp serverInfo command
  4. Find :foo :bar in the output

You have just modified the LSP server powering your editor while it was running! This is the Clojure way. No recompiling and restarting and reloading. That is some other, non-Clojure, way.

The details in how to perform these steps can vary a bit between the various Clojure editors/plugins.

Visual Studio Code with Calva

  • This project comes with Calva configuration to use the clojure-lsp executable built in step 1 above. You can skip step 2.
  • To restart the clojure-lsp server, use the VS Code command Developer: Reload Window
  • The Hack away! step needs to start with you issuing the command Calva: Load Current File and Dependencies.

Emacs with CIDER

  • To configure Emacs to use the nREPL-enabled executable, run (setq lsp-clojure-custom-server-command '("~/path/to/clojure-lsp/clojure-lsp")), adjusting the path as necessary. If you add this to your Emacs config, you can skip this step in the future.
  • To restart the clojure-lsp server, execute the Emacs command lsp-workspace-restart.
  • To find the server info, execute lsp-clojure-server-info.
  • To connect the nREPL client, run cider-connect-clj, with "localhost" and the port.

If you re-connect regulary, you may want to add this Emacs shortcut:

(defun lsp-clojure-nrepl-connect ()
  "Connect to the running nrepl debug server of clojure-lsp."
  (interactive)
  (let ((info (lsp-clojure-server-info-raw)))
    (save-match-data
      (when-let (port (and (string-match "\"port\":\\([0-9]+\\)" info)
                           (match-string 1 info)))
        (cider-connect-clj `(:host "localhost"
                             :port ,port))))))

Vim with coc.nvim and Fireplace

  • Change coc-settings.json (:CocConfig) clojure-lsp: {command: "~/path/to/clojure-lsp/clojure-lsp"}, adjusting the past as necessary.
  • To restart the clojure-lsp server use :CocRestart
  • To find the server info, :echo CocRequest('clojure-lsp', 'clojure/serverInfo/raw')['port']
  • To find the server log, :echo CocRequest('clojure-lsp', 'clojure/serverInfo/raw')['log-path']
  • To connect the nREPL client, run :Connect <port>

If you re-connect regulary, you may want to add something like this to your vimrc:

" Copies the log-path to your clipboard
nnoremap <silent> crsl :call setreg('*', CocRequest('clojure-lsp', 'clojure/serverInfo/raw')['log-path'])<CR>
" Connects to nrepl
nnoremap <silent> crsp :execute 'Connect' CocRequest('clojure-lsp', 'clojure/serverInfo/raw')['port']<CR>

Your Favorite Editor

TBD. PR welcome.

Profiling

To make a build with profiling and benchmarking tools available, in addition to an nREPL, add the following requires to the namespace you want to profile.

(:require
  ,,,
  [clj-async-profiler.core :as profiler]
  [criterium.core :as bench])

Also add an (unused) function

(defn stub []
  (bench/quick-bench (* 2 3))
  (profiler/profile
    {:min-width 5}
    (dotimes [_ 500]
      (* 2 3))))

Then run make debug-perf-cli. When the build has completed, follow the above steps to connect to an nREPL.

Execute profiling code within a rich comment block, using the code from the stub function as a guideline. You'll need to be familiar with criterium and clj-async-profiler to interpret the results.

Note that it is often important to do profiling with this kind of nREPL, because in a regular REPL the analysis database starts off nearly empty. With a fuller database, profiling is more accurate. That said, if you do want to profile in a regular REPL, you can—start it with the :performance alias.

Can you improve this documentation? These fine people already did:
Eric Dallo, Jacob Maine, Peter Strömberg & Case Nelson
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close