Skip to content
This repository was archived by the owner on Jul 28, 2022. It is now read-only.

Commit 531cf67

Browse files
authored
add before initalize hook (#96)
1 parent baab1fc commit 531cf67

File tree

3 files changed

+79
-13
lines changed

3 files changed

+79
-13
lines changed

README.md

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,31 @@ Running `lsp-adapter --help` shows you some of its options:
2929
Usage: lsp-adapter [OPTIONS] LSP_COMMAND_ARGS...
3030

3131
Options:
32+
-beforeInitializeHook string
33+
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.
3234
-cacheDirectory string
33-
cache directory location (default "/tmp/proxy-cache")
35+
cache directory location (default "/var/folders/qq/1q_cmsmx6qv7bs_m6g_2pt1r0000gn/T/proxy-cache")
3436
-didOpenLanguage string
35-
(HACK) If non-empty, send 'textDocument/didOpen' notifications with the specified language field (e.x. 'python') to the language server for every file.
37+
(HACK) If non-empty, send 'textDocument/didOpen' notifications with the specified language field (e.x. 'python') to the language server for every file.
3638
-glob string
37-
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.
39+
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.
3840
-jsonrpc2IDRewrite string
39-
(HACK) Rewrite jsonrpc2 ID. none (default) is no rewriting. string will use a string ID. number will use a number ID. Useful for language servers with non-spec complaint JSONRPC2 implementations. (default "none")
41+
(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")
42+
-pprofAddr string
43+
server listen address for pprof
4044
-proxyAddress string
41-
proxy server listen address (tcp) (default "127.0.0.1:8080")
45+
proxy server listen address (tcp) (default "127.0.0.1:8080")
4246
-trace
43-
trace logs to stderr
47+
trace logs to stderr (default true)
4448
```
49+
4550
## How to Use `lsp-adapter`
4651

4752
`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
4853

4954
- How to connect `lsp-adapter` to the language server
5055
- How to connect to your Sourcegraph instance to `lsp-adapter`
5156

52-
5357
### Connect `lsp-adapter` to the Language Server
5458

5559
`lsp-adapter` can talk to language servers over standard I/O.
@@ -64,19 +68,19 @@ lsp-adapter rls
6468

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

67-
6871
### Connect Sourcegraph to `lsp-adapter`
6972

70-
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`.
73+
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`.
7174

72-
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](https://about.sourcegraph.com/docs/code-intelligence/install-manual)). 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`:
75+
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](https://about.sourcegraph.com/docs/code-intelligence/install-manual)). 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`:
7376

7477
```json
7578
{
76-
"language": "rust",
77-
"address": "tcp://127.0.0.1:8080"
79+
"language": "rust",
80+
"address": "tcp://127.0.0.1:8080"
7881
}
7982
```
83+
8084
would be the new entry that needs to be added to the `"langservers"` field.
8185

8286
## Example Commands
@@ -111,7 +115,30 @@ Most language servers will only ever look at files that match a set of known pat
111115

112116
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](https://github.com/sourcegraph/lsp-adapter/commit/1228a1fbaf102aa44575cec6802a5a211d117ee1) 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.
113117

114-
115118
## JSONRPC2 ID Rewrite Hack
116119

117120
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.
121+
122+
## Before Initialization Hook
123+
124+
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).
125+
126+
For example, if you have the following script named `hook.sh`:
127+
128+
```shell
129+
#!/bin/sh
130+
set -x
131+
echo $(pwd)
132+
```
133+
134+
After specifying the hook via `lsp-adapter -beforeInitializeHook='/test.sh'` You will see output like
135+
136+
```shell
137+
2018/06/20 21:11:45 uris.go:25: Cloned workspace to /tmp/proxy-cache/3bb76102-a73b-4442-a03e-930c59e1fdcb
138+
2018/06/20 21:11:45 hook.go:26: Running pre-init hook: '/test.sh /tmp/proxy-cache/3bb76102-a73b-4442-a03e-930c59e1fdcb'
139+
++ pwd
140+
+ echo /Users/ggilmore/dev/go/src/github.com/sourcegraph/lsp-adapter
141+
/Users/ggilmore/dev/go/src/github.com/sourcegraph/lsp-adapter
142+
```
143+
144+
every time the language sever receives an `initialize` request. Obviously, you should replace this script with something meaningful.

hook.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"log"
6+
"os"
7+
"os/exec"
8+
9+
"github.com/pkg/errors"
10+
)
11+
12+
// runHook runs the specified "program" after the contents of the repository are cloned
13+
// to the workspace cache directory, but before the language server receives the "initialize"
14+
// request from Sourcegraph.
15+
//
16+
// The workspace cache directory is both:
17+
// - passed as an argument to "program"
18+
// - used as the cwd for "program"
19+
func (p *cloneProxy) runHook(ctx context.Context, program string) error {
20+
cmd := exec.CommandContext(ctx, program, p.workspaceCacheDir())
21+
22+
cmd.Dir = p.workspaceCacheDir()
23+
cmd.Stdout = os.Stdout
24+
cmd.Stderr = os.Stderr
25+
26+
log.Printf("Running pre-init hook: '%s %s'\n", program, p.workspaceCacheDir())
27+
err := cmd.Run()
28+
if err != nil {
29+
return errors.Wrapf(err, "When running pre-init hook: '%s %s'", program, p.workspaceCacheDir())
30+
}
31+
32+
return nil
33+
}

proxy.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ var (
3232
didOpenLanguage = flag.String("didOpenLanguage", "", "(HACK) If non-empty, send 'textDocument/didOpen' notifications with the specified language field (e.x. 'python') to the language server for every file.")
3333
jsonrpc2IDRewrite = flag.String("jsonrpc2IDRewrite", "none", "(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.")
3434
glob = flag.String("glob", "", "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.")
35+
beforeInitHook = flag.String("beforeInitializeHook", "", "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.")
3536
trace = flag.Bool("trace", true, "trace logs to stderr")
3637
)
3738

@@ -211,6 +212,11 @@ func (p *cloneProxy) handleClientRequest(ctx context.Context, conn *jsonrpc2.Con
211212
log.Println("CloneProxy.handleClientRequest(): cloning workspace failed during initialize", err)
212213
return
213214
}
215+
if *beforeInitHook != "" {
216+
if err := p.runHook(ctx, *beforeInitHook); err != nil {
217+
log.Println("CloneProxy.handleClientRequest(): running beforeInitializeHook failed", err)
218+
}
219+
}
214220
}
215221

216222
rTripper := roundTripper{

0 commit comments

Comments
 (0)