lsp_router 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +189 -0
- data/README.md +39 -0
- data/Rakefile +8 -0
- data/config/example.conf +12 -0
- data/exe/lsp_router +5 -0
- data/lib/lsp_router/cli.rb +31 -0
- data/lib/lsp_router/client_side.rb +50 -0
- data/lib/lsp_router/config.rb +69 -0
- data/lib/lsp_router/server_side.rb +66 -0
- data/lib/lsp_router/version.rb +3 -0
- data/lib/lsp_router.rb +94 -0
- data/sig/lsp_router.rbs +4 -0
- metadata +60 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d57746f846403f9ca2c5a45bba0ecd01fdb6e907e05095431063b8bc4a0a5f80
|
|
4
|
+
data.tar.gz: a3aa45e975f6db6712acd14edd1f5fcfa3603578572890c87c29d0e8abb34da9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 101ee8a0b48df239a88ee5a4a684f3591ab544a7e76e35cbea4ab30e12ef4f0711fbde52dca246e0f5729d49589ada0d8327cf242ede5e6ef3f3ce7a5c2e0dea
|
|
7
|
+
data.tar.gz: dd2ec59d88c78cd1dd5111a0a6437ce87136b3ebc6fbdd1723b6a92e6d4a2e9ae5a6eff1c2c01ac61337e3e42dee342b8a4566b81e13367b03eb6154bcba149e
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
|
|
4
|
+
Bundler/OrderedGems:
|
|
5
|
+
Enabled: false
|
|
6
|
+
|
|
7
|
+
Gemspec/DevelopmentDependencies:
|
|
8
|
+
EnforcedStyle: gems.rb
|
|
9
|
+
|
|
10
|
+
Layout/EmptyLineAfterGuardClause:
|
|
11
|
+
Enabled: false
|
|
12
|
+
|
|
13
|
+
Layout/ExtraSpacing:
|
|
14
|
+
AllowBeforeTrailingComments: true
|
|
15
|
+
|
|
16
|
+
Layout/LineLength:
|
|
17
|
+
Max: 130
|
|
18
|
+
|
|
19
|
+
Layout/MultilineMethodCallIndentation:
|
|
20
|
+
EnforcedStyle: indented_relative_to_receiver
|
|
21
|
+
|
|
22
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
|
23
|
+
Enabled: false
|
|
24
|
+
|
|
25
|
+
Layout/SpaceAroundOperators:
|
|
26
|
+
Enabled: false
|
|
27
|
+
|
|
28
|
+
Layout/SpaceBeforeBlockBraces:
|
|
29
|
+
Enabled: false
|
|
30
|
+
|
|
31
|
+
Layout/SpaceInsideBlockBraces:
|
|
32
|
+
Enabled: false
|
|
33
|
+
|
|
34
|
+
Layout/HashAlignment:
|
|
35
|
+
Enabled: false
|
|
36
|
+
EnforcedHashRocketStyle: table
|
|
37
|
+
EnforcedColonStyle: table
|
|
38
|
+
|
|
39
|
+
Layout/SpaceAfterNot:
|
|
40
|
+
Enabled: false
|
|
41
|
+
|
|
42
|
+
Layout/SpaceInsideHashLiteralBraces:
|
|
43
|
+
Enabled: false
|
|
44
|
+
|
|
45
|
+
Layout/SpaceInsidePercentLiteralDelimiters:
|
|
46
|
+
Enabled: false
|
|
47
|
+
|
|
48
|
+
Lint/SuppressedException:
|
|
49
|
+
AllowComments: true
|
|
50
|
+
|
|
51
|
+
Metrics/AbcSize:
|
|
52
|
+
Max: 60
|
|
53
|
+
|
|
54
|
+
Metrics/BlockNesting:
|
|
55
|
+
Enabled: false
|
|
56
|
+
|
|
57
|
+
Metrics/ClassLength:
|
|
58
|
+
Enabled: false
|
|
59
|
+
|
|
60
|
+
Metrics/CyclomaticComplexity:
|
|
61
|
+
Max: 20
|
|
62
|
+
|
|
63
|
+
Metrics/MethodLength:
|
|
64
|
+
Max: 100
|
|
65
|
+
|
|
66
|
+
Naming/AccessorMethodName:
|
|
67
|
+
Enabled: false
|
|
68
|
+
|
|
69
|
+
Naming/BlockForwarding:
|
|
70
|
+
Enabled: false
|
|
71
|
+
|
|
72
|
+
Naming/MethodParameterName:
|
|
73
|
+
Enabled: false
|
|
74
|
+
|
|
75
|
+
Metrics/ParameterLists:
|
|
76
|
+
Max: 20
|
|
77
|
+
|
|
78
|
+
Metrics/PerceivedComplexity:
|
|
79
|
+
Max: 100
|
|
80
|
+
|
|
81
|
+
Style/AndOr:
|
|
82
|
+
Enabled: false
|
|
83
|
+
|
|
84
|
+
Style/BlockDelimiters:
|
|
85
|
+
Enabled: false
|
|
86
|
+
|
|
87
|
+
Style/CaseLikeIf:
|
|
88
|
+
Enabled: false
|
|
89
|
+
|
|
90
|
+
Style/ClassAndModuleChildren:
|
|
91
|
+
Enabled: false
|
|
92
|
+
|
|
93
|
+
Style/ClassCheck:
|
|
94
|
+
Enabled: false
|
|
95
|
+
|
|
96
|
+
Style/ClassMethods:
|
|
97
|
+
Enabled: false
|
|
98
|
+
|
|
99
|
+
Style/CommentedKeyword:
|
|
100
|
+
Enabled: false
|
|
101
|
+
|
|
102
|
+
Style/FormatString:
|
|
103
|
+
Enabled: false
|
|
104
|
+
|
|
105
|
+
Style/FrozenStringLiteralComment:
|
|
106
|
+
Enabled: false
|
|
107
|
+
|
|
108
|
+
Style/GuardClause:
|
|
109
|
+
Enabled: false
|
|
110
|
+
|
|
111
|
+
Style/IfUnlessModifier:
|
|
112
|
+
Enabled: false
|
|
113
|
+
|
|
114
|
+
Style/InfiniteLoop:
|
|
115
|
+
Enabled: false
|
|
116
|
+
|
|
117
|
+
Style/Lambda:
|
|
118
|
+
Enabled: false
|
|
119
|
+
|
|
120
|
+
Style/MethodCallWithoutArgsParentheses:
|
|
121
|
+
Enabled: false
|
|
122
|
+
|
|
123
|
+
Style/MutableConstant:
|
|
124
|
+
Enabled: false
|
|
125
|
+
|
|
126
|
+
Style/NestedTernaryOperator:
|
|
127
|
+
Enabled: false
|
|
128
|
+
|
|
129
|
+
Style/Next:
|
|
130
|
+
Enabled: false
|
|
131
|
+
|
|
132
|
+
Style/Not:
|
|
133
|
+
Enabled: false
|
|
134
|
+
|
|
135
|
+
Style/NumericLiterals:
|
|
136
|
+
Enabled: false
|
|
137
|
+
|
|
138
|
+
Style/NumericPredicate:
|
|
139
|
+
Enabled: false
|
|
140
|
+
|
|
141
|
+
Style/ParallelAssignment:
|
|
142
|
+
Enabled: false
|
|
143
|
+
|
|
144
|
+
Style/PercentLiteralDelimiters:
|
|
145
|
+
Enabled: false
|
|
146
|
+
|
|
147
|
+
Style/PerlBackrefs:
|
|
148
|
+
Enabled: false
|
|
149
|
+
|
|
150
|
+
Style/PreferredHashMethods:
|
|
151
|
+
Enabled: false
|
|
152
|
+
|
|
153
|
+
Style/RedundantReturn:
|
|
154
|
+
Enabled: false
|
|
155
|
+
|
|
156
|
+
Style/RedundantSelf:
|
|
157
|
+
Enabled: false
|
|
158
|
+
|
|
159
|
+
Style/RescueModifier:
|
|
160
|
+
Enabled: false
|
|
161
|
+
|
|
162
|
+
Style/RescueStandardError:
|
|
163
|
+
Enabled: false
|
|
164
|
+
|
|
165
|
+
Style/SafeNavigation:
|
|
166
|
+
Enabled: false
|
|
167
|
+
|
|
168
|
+
Style/SpecialGlobalVars:
|
|
169
|
+
Enabled: false
|
|
170
|
+
|
|
171
|
+
Style/StringConcatenation:
|
|
172
|
+
Enabled: false
|
|
173
|
+
|
|
174
|
+
Style/StringLiterals:
|
|
175
|
+
Enabled: false
|
|
176
|
+
|
|
177
|
+
Style/TrailingCommaInArrayLiteral:
|
|
178
|
+
EnforcedStyleForMultiline: consistent_comma
|
|
179
|
+
|
|
180
|
+
Style/TrailingCommaInHashLiteral:
|
|
181
|
+
EnforcedStyleForMultiline: consistent_comma
|
|
182
|
+
|
|
183
|
+
Style/VariableInterpolation:
|
|
184
|
+
Enabled: false
|
|
185
|
+
|
|
186
|
+
Style/WordArray:
|
|
187
|
+
Enabled: false
|
|
188
|
+
|
|
189
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# LspRouter
|
|
2
|
+
|
|
3
|
+
複数のLSPサーバーに処理を振り分けるルーター
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
gem install lsp_router
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
次のような config ファイルを作成して、`lsp_router config-file` のように実行する。
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
logfile '/tmp/lsp_router'
|
|
17
|
+
loglevel :info
|
|
18
|
+
|
|
19
|
+
server :rubocop do
|
|
20
|
+
command 'rubocop-lsp'
|
|
21
|
+
mode :stdio
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
server :solargraph do
|
|
25
|
+
command 'solargraph stdio'
|
|
26
|
+
mode :stdio
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
この例では、`rubocop-lsp` と `solargraph stdio` を起動して、クライアントからの処理を振り分ける。
|
|
31
|
+
最初に各サーバーの capabilities を確認して、各サーバーにどの機能があるかを確認し、クライアントからの REQUEST は対応しているサーバーに渡す。複数のサーバーが同じ機能を持っていれば上に書いたサーバーが優先される。NOTIFICATION は全サーバーに渡す。
|
|
32
|
+
|
|
33
|
+
## Example
|
|
34
|
+
|
|
35
|
+
Emacs の Eglot の場合はこんな感じに設定するといいっぽい。
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
(add-to-list 'eglot-server-programs '((ruby-mode ruby-ts-mode) . ("lsp_router" "--error=lsp_router.err" "lsp_router.conf")))
|
|
39
|
+
```
|
data/Rakefile
ADDED
data/config/example.conf
ADDED
data/exe/lsp_router
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
|
|
3
|
+
class LspRouter
|
|
4
|
+
# command line interface
|
|
5
|
+
class CLI
|
|
6
|
+
def self.start
|
|
7
|
+
port = nil
|
|
8
|
+
logfile = nil
|
|
9
|
+
error = nil
|
|
10
|
+
|
|
11
|
+
opts = OptionParser.new
|
|
12
|
+
opts.version = LspRouter::VERSION
|
|
13
|
+
opts.banner = "Usage: #{File.basename $0} [options] config_file"
|
|
14
|
+
opts.on('-p', '--port=port'){|v| port = v}
|
|
15
|
+
opts.on('--logfile=file'){|v| logfile = v}
|
|
16
|
+
opts.on('--error=file'){|v| error = v}
|
|
17
|
+
opts.parse!(ARGV)
|
|
18
|
+
if error
|
|
19
|
+
$stderr = File.open(error, 'a')
|
|
20
|
+
$stderr.sync = true
|
|
21
|
+
end
|
|
22
|
+
if ARGV.size != 1
|
|
23
|
+
puts opts.help
|
|
24
|
+
exit 1
|
|
25
|
+
end
|
|
26
|
+
config_file, = ARGV
|
|
27
|
+
load config_file, LspRouter::Config::M
|
|
28
|
+
LspRouter.new(LspRouter::Config.instance, port:, logfile:).run
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
class LspRouter
|
|
2
|
+
# client side
|
|
3
|
+
class ClientSide
|
|
4
|
+
# @param rio [IO]
|
|
5
|
+
# @param wio [IO]
|
|
6
|
+
# @param logger [Logger]
|
|
7
|
+
def initialize(rio, wio, logger:)
|
|
8
|
+
@logger = logger
|
|
9
|
+
@rio = rio
|
|
10
|
+
@wio = wio
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Hash]
|
|
14
|
+
def read
|
|
15
|
+
header = @rio.gets("\r\n\r\n")
|
|
16
|
+
return unless header
|
|
17
|
+
fields = header.lines.map do |line|
|
|
18
|
+
n, v = line.chomp.split(/: */, 2)
|
|
19
|
+
[n.downcase, v] if n
|
|
20
|
+
end.compact.to_h
|
|
21
|
+
ret = @rio.read(fields['content-length'].to_i)
|
|
22
|
+
return unless ret
|
|
23
|
+
data = JSON.parse(ret)
|
|
24
|
+
log('client>', data)
|
|
25
|
+
data
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @param data [Hash]
|
|
29
|
+
def write(data)
|
|
30
|
+
log('client<', data)
|
|
31
|
+
json = data.to_json
|
|
32
|
+
@wio.puts "Content-Length: #{json.bytesize}\r\n\r\n"
|
|
33
|
+
@wio.write(json)
|
|
34
|
+
@wio.flush
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param prefix [String]
|
|
38
|
+
# @param data [Hash]
|
|
39
|
+
def log(prefix, data)
|
|
40
|
+
if data['id'] && data['method']
|
|
41
|
+
@logger.debug "#{prefix}[REQUEST] id:#{data['id']} method:#{data['method']}"
|
|
42
|
+
elsif data['id']
|
|
43
|
+
@logger.debug "#{prefix}[RESPONSE] id:#{data['id']}"
|
|
44
|
+
else
|
|
45
|
+
@logger.debug "#{prefix}[NOTIFICATION] method:#{data['method']}"
|
|
46
|
+
end
|
|
47
|
+
@logger.debug data.inspect
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
class LspRouter
|
|
4
|
+
# config
|
|
5
|
+
class Config
|
|
6
|
+
include Singleton
|
|
7
|
+
|
|
8
|
+
attr_reader :servers
|
|
9
|
+
attr_accessor :logfile, :loglevel
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@servers = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @param name [String]
|
|
16
|
+
def add_server(name, &block)
|
|
17
|
+
@servers.push Server.new(name, &block)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# module for configuration file
|
|
21
|
+
module M
|
|
22
|
+
# @param name [String]
|
|
23
|
+
def server(name, &block)
|
|
24
|
+
config.add_server(name, &block)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param filename [String]
|
|
28
|
+
def logfile(filename)
|
|
29
|
+
config.logfile = filename
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param level [Symbol]
|
|
33
|
+
def loglevel(level)
|
|
34
|
+
config.loglevel = level
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [LspRouter::Config]
|
|
38
|
+
def config
|
|
39
|
+
Config.instance
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# server
|
|
44
|
+
class Server
|
|
45
|
+
attr_reader :attr
|
|
46
|
+
|
|
47
|
+
# @param name [String]
|
|
48
|
+
def initialize(name, &block)
|
|
49
|
+
@attr = {name:}
|
|
50
|
+
self.instance_eval(&block)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param cmd [String]
|
|
54
|
+
def command(cmd)
|
|
55
|
+
@attr[:command] = cmd
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @param m [Symbol]
|
|
59
|
+
def mode(m)
|
|
60
|
+
@attr[:mode] = m
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [String]
|
|
64
|
+
def info
|
|
65
|
+
"[#{attr[:name]}] #{attr[:command]}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'open3'
|
|
2
|
+
|
|
3
|
+
class LspRouter
|
|
4
|
+
# server side
|
|
5
|
+
class ServerSide
|
|
6
|
+
# @return String
|
|
7
|
+
attr_reader :name
|
|
8
|
+
# @return [Hash]
|
|
9
|
+
attr_reader :capabilities
|
|
10
|
+
|
|
11
|
+
# @param server [LspRouter::Config::Server]
|
|
12
|
+
# @param logger [Logger]
|
|
13
|
+
def initialize(server, logger:)
|
|
14
|
+
@logger = logger
|
|
15
|
+
@name = server.attr[:name]
|
|
16
|
+
@command = server.attr[:command]
|
|
17
|
+
@stdin, @stdout, @stderr = Open3.popen3(@command)
|
|
18
|
+
@capabilities = {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @param req [Hash]
|
|
22
|
+
def init(req)
|
|
23
|
+
write(req)
|
|
24
|
+
data = read
|
|
25
|
+
capa = data.dig('result', 'capabilities')
|
|
26
|
+
capa.keys.grep(/Provider/).each do |key|
|
|
27
|
+
@capabilities[key.sub(/Provider\z/, '')] = true
|
|
28
|
+
end
|
|
29
|
+
data
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param data [Hash]
|
|
33
|
+
def write(data)
|
|
34
|
+
log("#{name}<", data)
|
|
35
|
+
json = data.to_json
|
|
36
|
+
@stdin.puts "Content-Length: #{json.bytesize}\r\n\r\n"
|
|
37
|
+
@stdin.write(json)
|
|
38
|
+
@stdin.flush
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Hash]
|
|
42
|
+
def read
|
|
43
|
+
header = @stdout.gets("\r\n\r\n")
|
|
44
|
+
fields = header.lines.map do |line|
|
|
45
|
+
n, v = line.chomp.split(/: */, 2)
|
|
46
|
+
[n.downcase, v] if n
|
|
47
|
+
end.compact.to_h
|
|
48
|
+
data = JSON.parse(@stdout.read(fields['content-length'].to_i))
|
|
49
|
+
log("#{name}>", data)
|
|
50
|
+
data
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param prefix [String]
|
|
54
|
+
# @param data [Hash]
|
|
55
|
+
def log(prefix, data)
|
|
56
|
+
if data['id'] && data['method']
|
|
57
|
+
@logger.debug "#{prefix}[REQUEST] id:#{data['id']} method:#{data['method']}"
|
|
58
|
+
elsif data['id']
|
|
59
|
+
@logger.debug "#{prefix}[RESPONSE] id:#{data['id']}"
|
|
60
|
+
else
|
|
61
|
+
@logger.debug "#{prefix}[NOTIFICATION] method:#{data['method']}"
|
|
62
|
+
end
|
|
63
|
+
@logger.debug data.inspect
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/lsp_router.rb
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require_relative 'lsp_router/version'
|
|
2
|
+
require_relative 'lsp_router/config'
|
|
3
|
+
require_relative 'lsp_router/server_side'
|
|
4
|
+
require_relative 'lsp_router/client_side'
|
|
5
|
+
|
|
6
|
+
require 'socket'
|
|
7
|
+
require 'json'
|
|
8
|
+
require 'logger'
|
|
9
|
+
|
|
10
|
+
# LspRouter
|
|
11
|
+
class LspRouter
|
|
12
|
+
# @param config [LspRouter::Config] configuration filename
|
|
13
|
+
# @param port [Integer, String]
|
|
14
|
+
# @param logfile [String]
|
|
15
|
+
def initialize(config, port: nil, logfile: nil)
|
|
16
|
+
@config = config
|
|
17
|
+
@port = port
|
|
18
|
+
logfile ||= config.logfile || $stderr
|
|
19
|
+
@logger = Logger.new(logfile, level: Logger.const_get(config.loglevel.upcase))
|
|
20
|
+
@logger.info "LspRouter: start"
|
|
21
|
+
@config.servers.each do |server|
|
|
22
|
+
@logger.info "server: #{server.info}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run
|
|
27
|
+
if @port
|
|
28
|
+
@logger.info "mode: port:#{@port}"
|
|
29
|
+
Socket.tcp_server_loop(@port) do |sock, _|
|
|
30
|
+
@client = LspRouter::ClientSide.new(sock, sock, logger: @logger)
|
|
31
|
+
loop
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
@logger.info "mode: stdio"
|
|
35
|
+
@client = LspRouter::ClientSide.new($stdin, $stdout, logger: @logger)
|
|
36
|
+
loop
|
|
37
|
+
end
|
|
38
|
+
rescue => e
|
|
39
|
+
@logger.error e
|
|
40
|
+
ensure
|
|
41
|
+
@logger.info "LspRouter: stop"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def loop
|
|
45
|
+
req = @client.read
|
|
46
|
+
servers = @config.servers.map{LspRouter::ServerSide.new(_1, logger: @logger)}
|
|
47
|
+
primary = servers.first
|
|
48
|
+
|
|
49
|
+
res = nil
|
|
50
|
+
capa_server = {}
|
|
51
|
+
servers.each do |server|
|
|
52
|
+
data = server.init(req)
|
|
53
|
+
if res
|
|
54
|
+
res['result']['capabilities'].update(data.dig('result', 'capabilities')){|_, v, _| v}
|
|
55
|
+
else
|
|
56
|
+
res = data
|
|
57
|
+
end
|
|
58
|
+
server.capabilities.each_key do |key|
|
|
59
|
+
capa_server[key] ||= server
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
@client.write(res)
|
|
63
|
+
|
|
64
|
+
thr = Thread.new do
|
|
65
|
+
Thread.abort_on_exception = true
|
|
66
|
+
while true
|
|
67
|
+
data = @client.read
|
|
68
|
+
break unless data
|
|
69
|
+
if data['id']
|
|
70
|
+
# request
|
|
71
|
+
method = data['method']&.split('/')&.last
|
|
72
|
+
server = capa_server[method] || primary
|
|
73
|
+
server.write(data)
|
|
74
|
+
else
|
|
75
|
+
# notification
|
|
76
|
+
servers.each do |server|
|
|
77
|
+
server.write(data)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
servers.each do |server|
|
|
84
|
+
Thread.new do
|
|
85
|
+
Thread.abort_on_exception = true
|
|
86
|
+
while true
|
|
87
|
+
data = server.read
|
|
88
|
+
@client.write(data)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
thr.join
|
|
93
|
+
end
|
|
94
|
+
end
|
data/sig/lsp_router.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lsp_router
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- TOMITA Masahiro
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2023-12-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: routing multiple LSP server by capability
|
|
14
|
+
email:
|
|
15
|
+
- tommy@tmtm.org
|
|
16
|
+
executables:
|
|
17
|
+
- lsp_router
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- ".rspec"
|
|
22
|
+
- ".rubocop.yml"
|
|
23
|
+
- README.md
|
|
24
|
+
- Rakefile
|
|
25
|
+
- config/example.conf
|
|
26
|
+
- exe/lsp_router
|
|
27
|
+
- lib/lsp_router.rb
|
|
28
|
+
- lib/lsp_router/cli.rb
|
|
29
|
+
- lib/lsp_router/client_side.rb
|
|
30
|
+
- lib/lsp_router/config.rb
|
|
31
|
+
- lib/lsp_router/server_side.rb
|
|
32
|
+
- lib/lsp_router/version.rb
|
|
33
|
+
- sig/lsp_router.rbs
|
|
34
|
+
homepage: https://gitlab.com/tmtms/lsp_router
|
|
35
|
+
licenses:
|
|
36
|
+
- GPLv3
|
|
37
|
+
metadata:
|
|
38
|
+
homepage_uri: https://gitlab.com/tmtms/lsp_router
|
|
39
|
+
source_code_uri: https://gitlab.com/tmtms/lsp_router
|
|
40
|
+
rubygems_mfa_required: 'true'
|
|
41
|
+
post_install_message:
|
|
42
|
+
rdoc_options: []
|
|
43
|
+
require_paths:
|
|
44
|
+
- lib
|
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '3.1'
|
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
requirements: []
|
|
56
|
+
rubygems_version: 3.5.0.dev
|
|
57
|
+
signing_key:
|
|
58
|
+
specification_version: 4
|
|
59
|
+
summary: LSP router
|
|
60
|
+
test_files: []
|