lsp_router 0.1.0 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -2
- data/config/example.conf +1 -1
- data/lib/lsp_router/server_side.rb +7 -0
- data/lib/lsp_router/version.rb +1 -1
- data/lib/lsp_router.rb +44 -17
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b81686d10b1edcb9c6b6ab51eedd3f8bd4ca71c3bd3d44b820fd416f2a48f0e
|
4
|
+
data.tar.gz: 85e81047822d36218ac021ccccb240af5fdc896dcb0218a8c835f613343dea1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 289f4683fc2aead84ac11c7655b1faeb142f3692d26af1683d62181a3fb6ea7ac345f016e1e27946c5229dd755c9e49afed0ef4d00b5b28a481e42b6763dc933
|
7
|
+
data.tar.gz: c1e65b91db8a71ba1ebfacd134dc2b3a133d569a7ae67346fcc7a004502a4eff1fba2924d755f9f14420f3ff6369e89bb36f60a60a01f014f5249aba160f7e02
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ logfile '/tmp/lsp_router'
|
|
17
17
|
loglevel :info
|
18
18
|
|
19
19
|
server :rubocop do
|
20
|
-
command 'rubocop
|
20
|
+
command 'rubocop --lsp'
|
21
21
|
mode :stdio
|
22
22
|
end
|
23
23
|
|
@@ -27,7 +27,7 @@ server :solargraph do
|
|
27
27
|
end
|
28
28
|
```
|
29
29
|
|
30
|
-
この例では、`rubocop
|
30
|
+
この例では、`rubocop --lsp` と `solargraph stdio` を起動して、クライアントからの処理を振り分ける。
|
31
31
|
最初に各サーバーの capabilities を確認して、各サーバーにどの機能があるかを確認し、クライアントからの REQUEST は対応しているサーバーに渡す。複数のサーバーが同じ機能を持っていれば上に書いたサーバーが優先される。NOTIFICATION は全サーバーに渡す。
|
32
32
|
|
33
33
|
## Example
|
@@ -37,3 +37,7 @@ Emacs の Eglot の場合はこんな感じに設定するといいっぽい。
|
|
37
37
|
```
|
38
38
|
(add-to-list 'eglot-server-programs '((ruby-mode ruby-ts-mode) . ("lsp_router" "--error=lsp_router.err" "lsp_router.conf")))
|
39
39
|
```
|
40
|
+
|
41
|
+
## License
|
42
|
+
|
43
|
+
GPLv3
|
data/config/example.conf
CHANGED
@@ -16,6 +16,7 @@ class LspRouter
|
|
16
16
|
@command = server.attr[:command]
|
17
17
|
@stdin, @stdout, @stderr = Open3.popen3(@command)
|
18
18
|
@capabilities = {}
|
19
|
+
@terminated = false
|
19
20
|
end
|
20
21
|
|
21
22
|
# @param req [Hash]
|
@@ -31,6 +32,7 @@ class LspRouter
|
|
31
32
|
|
32
33
|
# @param data [Hash]
|
33
34
|
def write(data)
|
35
|
+
return if @terminated
|
34
36
|
log("#{name}<", data)
|
35
37
|
json = data.to_json
|
36
38
|
@stdin.puts "Content-Length: #{json.bytesize}\r\n\r\n"
|
@@ -40,7 +42,12 @@ class LspRouter
|
|
40
42
|
|
41
43
|
# @return [Hash]
|
42
44
|
def read
|
45
|
+
return nil if @terminated
|
43
46
|
header = @stdout.gets("\r\n\r\n")
|
47
|
+
unless header
|
48
|
+
@terminated = true
|
49
|
+
return nil
|
50
|
+
end
|
44
51
|
fields = header.lines.map do |line|
|
45
52
|
n, v = line.chomp.split(/: */, 2)
|
46
53
|
[n.downcase, v] if n
|
data/lib/lsp_router/version.rb
CHANGED
data/lib/lsp_router.rb
CHANGED
@@ -42,13 +42,21 @@ class LspRouter
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def loop
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
@servers = @config.servers.map{LspRouter::ServerSide.new(_1, logger: @logger)}
|
46
|
+
@primary = @servers.first
|
47
|
+
@server_map = @servers.map.to_h{[_1.name.to_s, _1]}
|
48
|
+
@capa_server = {}
|
49
|
+
|
50
|
+
initial_packet
|
51
|
+
threads = server_loop
|
52
|
+
threads.push client_loop
|
53
|
+
threads.each(&:join)
|
54
|
+
end
|
48
55
|
|
56
|
+
def initial_packet
|
57
|
+
req = @client.read
|
49
58
|
res = nil
|
50
|
-
|
51
|
-
servers.each do |server|
|
59
|
+
@servers.each do |server|
|
52
60
|
data = server.init(req)
|
53
61
|
if res
|
54
62
|
res['result']['capabilities'].update(data.dig('result', 'capabilities')){|_, v, _| v}
|
@@ -56,39 +64,58 @@ class LspRouter
|
|
56
64
|
res = data
|
57
65
|
end
|
58
66
|
server.capabilities.each_key do |key|
|
59
|
-
capa_server[key] ||= server
|
67
|
+
@capa_server[key] ||= server
|
60
68
|
end
|
61
69
|
end
|
62
70
|
@client.write(res)
|
71
|
+
end
|
63
72
|
|
64
|
-
|
73
|
+
def client_loop
|
74
|
+
Thread.new do
|
65
75
|
Thread.abort_on_exception = true
|
66
76
|
while true
|
67
77
|
data = @client.read
|
68
78
|
break unless data
|
69
|
-
if data['
|
70
|
-
|
79
|
+
if data['method'] == 'shutdown' || data['method'] == 'exit'
|
80
|
+
@servers.each{_1.write(data)}
|
81
|
+
break if data['method'] == 'exit'
|
82
|
+
next
|
83
|
+
end
|
84
|
+
if data['id'] # request or response
|
71
85
|
method = data['method']&.split('/')&.last
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
#
|
76
|
-
|
77
|
-
|
86
|
+
if data['method'] # request
|
87
|
+
server = @capa_server[method] || @primary
|
88
|
+
data['id'] = [server.name, data['id']].to_json
|
89
|
+
else # response
|
90
|
+
server_name, id = JSON.parse(data['id'])
|
91
|
+
data['id'] = id
|
92
|
+
server = @server_map[server_name]
|
78
93
|
end
|
94
|
+
server.write(data)
|
95
|
+
else # notification
|
96
|
+
@servers.each{_1.write(data)}
|
79
97
|
end
|
80
98
|
end
|
99
|
+
@logger.info "client: end"
|
81
100
|
end
|
101
|
+
end
|
82
102
|
|
83
|
-
|
103
|
+
def server_loop
|
104
|
+
@servers.map do |server|
|
84
105
|
Thread.new do
|
85
106
|
Thread.abort_on_exception = true
|
86
107
|
while true
|
87
108
|
data = server.read
|
109
|
+
break unless data
|
110
|
+
if data['id'] && data['method'] # request
|
111
|
+
data['id'] = [server.name, data['id']].to_json
|
112
|
+
elsif data['id'] # response
|
113
|
+
data['id'] = JSON.parse(data['id']).last rescue data['id']
|
114
|
+
end
|
88
115
|
@client.write(data)
|
89
116
|
end
|
117
|
+
@logger.info "server: [#{server.name}] end"
|
90
118
|
end
|
91
119
|
end
|
92
|
-
thr.join
|
93
120
|
end
|
94
121
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lsp_router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TOMITA Masahiro
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
11
|
+
date: 2023-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: routing multiple LSP server by capability
|
14
14
|
email:
|