Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cl-lsp

A Common Lisp client for stdio Language Server Protocol servers, extracted from Autolith. Use it to initialize a server, synchronize UTF-8 files, make JSON-RPC requests, and collect independent push and pull diagnostic reports.

Installation

Use SBCL on Linux, macOS, or Windows. Dependencies are argo, Bordeaux Threads, Quri, and Serapeum. ASDF supplies UIOP. JSON values follow argo’s model: objects are string-keyed hash tables, arrays are vectors, false is (argo:json-false), which json-get reads as NIL, and null is NIL.

Clone this repository into your ASDF source registry and install the dependencies with Quicklisp, or run qlot install in the checkout for the locked dependencies. Then load the system:

(asdf:load-system :cl-lsp)

Client usage

Supply the server command, its arguments, a language identifier, and an absolute project root. Initialization options and server settings are string-keyed JSON objects; construct them with json-object.

(let* ((root #p"/home/user/project/")
       (server (make-instance 'cl-lsp:lsp-server-configuration
                              :name "clangd"
                              :command "clangd"
                              :arguments '("--background-index")
                              :extensions '(".c" ".h" ".cpp")
                              :language-id "cpp"
                              :timeout-seconds 10))
       (client (cl-lsp:lsp-client-start server root
                                       :client-name "My editor"
                                       :client-version "1.0")))
  (unwind-protect
       (let* ((path (merge-pathnames "main.cpp" root))
              (document (cl-lsp:lsp-client-sync client path))
              (hover (cl-lsp:lsp-transport-request
                      (cl-lsp:lsp-client-transport client) "textDocument/hover"
                      (cl-lsp:json-object
                       "textDocument" (cl-lsp:json-object "uri" (cl-lsp:lsp-path-uri path))
                       "position" (cl-lsp:json-object "line" 0 "character" 0))
                      :timeout 10)))
         (values hover (cl-lsp:lsp-client-diagnostics client document :wait-seconds 1)))
    (cl-lsp:lsp-client-close client)))

The default client identity is cl-lsp version 0.1.0. Positions use zero-based UTF-16 units; lsp-position returns the position at the end of a string. lsp-path-uri percent-encodes an absolute native pathname, including Windows drive and UNC paths.

Call lsp-client-sync after saving a file. The client sends open/change/save notifications according to the server’s advertised synchronization mode. lsp-client-resync updates files already opened by that client.

lsp-client-diagnostics returns a JSON object with push and pull reports. Each report records its state, versioned flag, truncated flag, and items. Push reports may be pending, received, or unversioned; an empty received report is distinct from a pending report. Explicitly stale versions are discarded.

Configuration files and queries

lsp-read-configurations reads a declarative server list from a file through a closed, byte-bounded grammar with no evaluation:

(:version 1
 :servers ((:name "clangd" :command "clangd" :arguments ("--background-index")
            :extensions (".c" ".h") :language-id "cpp" :root-markers ("compile_commands.json")
            :settings "{\"clangd\": {}}" :timeout-seconds 10)))

:initialization-options and :settings are JSON object text, :disabled-p excludes a server from selection, and every bound is an exported *lsp-configuration-* variable. Malformed files signal lsp-configuration-error, which names the server and field when known.

lsp-configurations-for-path selects the enabled servers handling a file by its extension, and lsp-manager-map-file runs a function with a synchronized client and document for each of them, rooted with lsp-project-root below a boundary, returning one (:server :root :result) or (:server :error) row per server. lsp-client-query runs one of the read-only *lsp-query-operations* (definition, references, hover, implementation, type definition, document and workspace symbols) after checking lsp-client-supports-p, and lsp-text-position turns a zero-based line and UTF-16 character into a protocol position, the inverse of lsp-position.

Transport and client pools

Use lsp-transport-open with :command, :arguments, :directory, :request-handler, and :notification-handler for a lower-level JSON-RPC connection. Call lsp-transport-request with a timeout, or lsp-transport-notify for a notification. Close it with lsp-transport-close. lsp-read-message and lsp-write-message accept binary streams for framed JSON.

Use lsp-manager-client to reuse a live client for a server name/project root pair or restart a dead client. lsp-project-root picks that root: the nearest directory between a file and the workspace holding one of the server’s :root-markers, given canonical pathnames, falling back to the workspace. Construct lsp-manager with :client-name and :client-version to forward an application identity. Serialize pool access with lsp-manager-lock using Bordeaux Threads’ recursive lock protocol. Call lsp-manager-close to close the pool.

Bounds and errors

Configure the exported *lsp-maximum-* variables for header/body sizes, stderr retention, callback queues, documents, diagnostics, client counts, and the project-root search depth. Transport failures signal lsp-error; remote failures signal lsp-rpc-error; request deadlines signal lsp-timeout. Close clients in unwind-protect.

JSON objects use hash tables, arrays use vectors, true uses t, null uses nil, and explicit false uses yason:false. Decoded false is retained internally as :json-false for lossless re-encoding. json-get presents it as nil; json-get-present additionally returns whether the key exists.

Tests

Run ./script/check from the checkout, or sbcl --script script/check on Windows. The same cases run through (asdf:test-system :cl-lsp) after loading the dependencies. They cover framing, actual subprocess lifecycle and cleanup, UTF-16 synchronization, diagnostic source independence, client reuse/restart, application identity, file URIs, and JSON value preservation.

License

COLL-Attribution. See LICENSE.lisp for the complete terms.

About

LSP paperwork for Common Lisp

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages