sourcegraph / lsp-adapter

lsp-adapter provides a proxy which adapts Sourcegraph LSP requests to vanilla LSP requests

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

lsp-adapter Travis-CI AppVeyor GoDoc Go Report Card

Command lsp-adapter provides a proxy which adapts Sourcegraph LSP requests to vanilla LSP requests.

Background

Code Intelligence on Sourcegraph is powered by the Language Server Protocol.

Previously, language servers that were used on sourcegraph.com were additionally required to support our custom LSP files extensions. These extensions allowed language servers to operate without sharing a physical file system with the client. While it's preferable for language servers to implement these extensions for performance reasons, implementing this functionality is a large undertaking.

lsp-adapter eliminates the need for this requirement, which allows off-the-shelf language servers to be able to provide basic functionality (hovers, local definitions) to Sourcegraph.

How to Install

You can download the latest binary on the releases page.

Alternatively, install it with go get:

go get -u -v github.com/sourcegraph/lsp-adapter

Running lsp-adapter --help shows you some of its options:

> lsp-adapter --help
Usage: lsp-adapter [OPTIONS] LSP_COMMAND_ARGS...

Options:
  -beforeInitializeHook string
    	A program to run after cloning the repository, but before the 'initialize' call is forwarded to the language server. (For example, you can use this to run a script to install dependencies for the project). The program's cwd will be the workspace's cache directory, and it will also be passed the cache directory as an argument.
  -cacheDirectory string
    	cache directory location (default "/var/folders/qq/1q_cmsmx6qv7bs_m6g_2pt1r0000gn/T/proxy-cache")
  -didOpenLanguage string
    	(HACK) If non-empty, send 'textDocument/didOpen' notifications with the specified language field (e.x. 'python') to the language server for every file.
  -glob string
    	A colon (:) separated list of file globs to sync locally. By default we place all files into the workspace, but some language servers may only look at a subset of files. Specifying this allows us to avoid syncing all files. Note: This is done by basename only.
  -jsonrpc2IDRewrite string
    	(HACK) Rewrite jsonrpc2 ID. none (default) is no rewriting. string will use a string ID. number will use number ID. Useful for language servers with non-spec complaint JSONRPC2 implementations. (default "none")
  -pprofAddr string
    	server listen address for pprof
  -proxyAddress string
    	proxy server listen address (tcp) (default "127.0.0.1:8080")
  -trace
    	trace logs to stderr (default true)

How to Use lsp-adapter

lsp-adapter proxies requests between your Sourcegraph instance and the language server, and modifies them in such a way that allows for the two to communicate correctly. In order to do this, we need to know

  • How to connect lsp-adapter to the language server
  • How to connect to your Sourcegraph instance to lsp-adapter

Connect lsp-adapter to the Language Server

lsp-adapter can talk to language servers over standard I/O.

lsp-adapter interprets any positional arguments after the flags as the necessary command (+ arguments) to start the language server binary. It uses this command to communicate to the language server inside of a subprocess.

For example, if I am trying to use Rust’s language server, the command to start it up is just rls. lsp-adapter can be told to start the start the same server via:

lsp-adapter rls

Any stderr output from the binary will also appear in lsp-adapter's logs.

Connect Sourcegraph to lsp-adapter

  1. Use the -proxyAddress flag to tell lsp-adapter what address to listen for connections from Sourcegraph on. For example, I can tell lsp-adapter to listen on my local 8080 port with -proxyAddress=127.0.0.1:8080.

  2. We then need to add a new entry to the "langservers" field in the site configuration in order to point Sourcegraph at lsp-adapter (similar to the steps in this document). For example, if lsp-adapter is connected to the Rust language server, and the lsp-adapter itself is listening on 127.0.0.1:8080:

{
  "language": "rust",
  "address": "tcp://127.0.0.1:8080"
}

would be the new entry that needs to be added to the "langservers" field.

Example Commands

Connect via standard I/O to a language server whose command can be run with rls, and listen for connections from Sourcegraph from any address on port 1234.

> lsp-adapter -proxyAddresss=0.0.0.0:1234 rls

2018/04/04 15:27:04 proxy.go:71: CloneProxy: accepting connections at [::]:8080

Connect via standard I/O to a language server whose command can be run with rls, change the location of the cache directory (used for cloning the repo locally) to /tmpDir, and enable tracing for every request to/from the language server.

> lsp-adapter -cacheDir='/tmpDir' -trace rls

Docker

There is a skeleton Dockerfile that shows how to package lsp-adapter along with your desired language server inside of a docker container. There are fully working examples in dockerfiles. For example dockerfiles/rust/Dockerfile, which can be built with:

> docker build -f dockerfiles/rust/Dockerfile .

Glob

Most language servers will only ever look at files that match a set of known patterns. On initialize lsp-adapter copies a full work-tree to disk for a repository, but by specifying -glob we can avoid copying over files that will not be looked at. For example, if a python language server only looks at py and pyc files you can specify -glob=*.py:*.pyc. The matching is done on the basename of the path using path.Match.

Did Open Hack

Some language servers do not follow the LSP spec correctly and refuse to work unless the textDocument/didOpen notification has been sent. See this commit for more context. If the language server that you’re trying to use has this issue, try setting the didOpenLanguage flag (example: if a python language server had this issue - use ./lsp-adapter -didOpenLanguage=python ...) to work around it.

JSONRPC2 ID Rewrite Hack

Some language servers do not follow the JSONRPC2 spec correctly and fail if the Request ID is not a number of string. If the language server that you’re trying to use has this issue, try setting the jsonrpc2IDRewrite flag (example: if a rust language server had this issue - use ./lsp-adapter -jsonrpc2IDRewrite=number ...) to work around it.

Before Initialization Hook

Some language servers need to run setup scripts (to install dependencies, for example) in order to provide code intelligence. By using the -beforeInitializeHook flag, you can specify a program/script that will run after the repository is cloned to workspace's cache directory, but before the language server receives the initalize request. The program's cwd will be the cache directory (which will also be passed as an argument for convenience).

For example, if you have the following script named hook.sh:

#!/bin/sh
set -x
echo $(pwd)

After specifying the hook via lsp-adapter -beforeInitializeHook='/test.sh' You will see output like

2018/06/20 21:11:45 uris.go:25: Cloned workspace to /tmp/proxy-cache/3bb76102-a73b-4442-a03e-930c59e1fdcb
2018/06/20 21:11:45 hook.go:26: Running pre-init hook: '/test.sh /tmp/proxy-cache/3bb76102-a73b-4442-a03e-930c59e1fdcb'
++ pwd
+ echo /Users/ggilmore/dev/go/src/github.com/sourcegraph/lsp-adapter
/Users/ggilmore/dev/go/src/github.com/sourcegraph/lsp-adapter

every time the language sever receives an initialize request. Obviously, you should replace this script with something meaningful.

About

lsp-adapter provides a proxy which adapts Sourcegraph LSP requests to vanilla LSP requests

License:MIT License


Languages

Language:Go 73.7%Language:Dockerfile 22.1%Language:Shell 4.3%