lsp_router 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|