ruby-lsp 0.4.2 → 0.4.4
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 +4 -4
- data/README.md +29 -35
- data/VERSION +1 -1
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +62 -0
- data/lib/ruby_lsp/document.rb +74 -6
- data/lib/ruby_lsp/event_emitter.rb +52 -0
- data/lib/ruby_lsp/executor.rb +60 -14
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listener.rb +39 -0
- data/lib/ruby_lsp/requests/base_request.rb +2 -85
- data/lib/ruby_lsp/requests/code_action_resolve.rb +46 -19
- data/lib/ruby_lsp/requests/code_actions.rb +5 -4
- data/lib/ruby_lsp/requests/code_lens.rb +135 -0
- data/lib/ruby_lsp/requests/diagnostics.rb +3 -3
- data/lib/ruby_lsp/requests/document_highlight.rb +7 -9
- data/lib/ruby_lsp/requests/document_link.rb +7 -7
- data/lib/ruby_lsp/requests/document_symbol.rb +13 -10
- data/lib/ruby_lsp/requests/folding_ranges.rb +13 -9
- data/lib/ruby_lsp/requests/formatting.rb +5 -4
- data/lib/ruby_lsp/requests/hover.rb +28 -32
- data/lib/ruby_lsp/requests/inlay_hints.rb +5 -4
- data/lib/ruby_lsp/requests/path_completion.rb +16 -10
- data/lib/ruby_lsp/requests/selection_ranges.rb +3 -3
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +29 -15
- data/lib/ruby_lsp/requests/support/common.rb +55 -0
- data/lib/ruby_lsp/requests/support/highlight_target.rb +5 -4
- data/lib/ruby_lsp/requests/support/rails_document_client.rb +7 -6
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -1
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
- data/lib/ruby_lsp/requests/support/sorbet.rb +5 -15
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +39 -0
- data/lib/ruby_lsp/requests.rb +3 -0
- data/lib/ruby_lsp/server.rb +4 -1
- data/lib/ruby_lsp/store.rb +10 -10
- metadata +11 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61c8fe2a55057823533a8a17b4472309e3d463ec3d63302fa3393f046ec4b339
|
4
|
+
data.tar.gz: fb9636b8aafac9d5002a872d0f34f7f9852e9348009cdc2934369637a4cadb4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db51fd5526431b773125c0b27bfe8ca53b2861759457f9a36e7a681bcfbffb0f58026b4f8cc4dc991be2380309959aacde7c8cc2cd0a0f0373dfb313d0d28db9
|
7
|
+
data.tar.gz: 13cc3991bffd1a18dd1e2580c8d1051161b03a171a25e8ce681f46fb8d5baedb1de0f9f96304556e08f3db95c2262e29693d22bf3659ba45eff620157adc035c
|
data/README.md
CHANGED
@@ -1,57 +1,51 @@
|
|
1
|
-

|
1
|
+
[](https://github.com/Shopify/ruby-lsp/actions/workflows/ci.yml)
|
2
|
+
[](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
|
3
|
+
[](https://join.slack.com/t/ruby-dx/shared_invite/zt-1s6f4y15t-v9jedZ9YUPQLM91TEJ4Gew)
|
2
4
|
|
3
|
-
# Ruby LSP
|
4
|
-
|
5
|
-
This gem is an implementation of the [language server protocol specification](https://microsoft.github.io/language-server-protocol/) for Ruby, used to improve editor features.
|
6
|
-
|
7
|
-
# Overview
|
8
|
-
|
9
|
-
The intention of Ruby LSP is to provide a fast, robust and feature-rich coding environment for Ruby developers.
|
10
|
-
|
11
|
-
It's part of a [wider Shopify goal](https://github.com/Shopify/vscode-shopify-ruby) to provide a state-of-the-art experience to Ruby developers using modern standards for cross-editor features, documentation and debugging.
|
12
5
|
|
13
|
-
|
14
|
-
|
15
|
-
* Syntax highlighting
|
16
|
-
* Linting and formatting
|
17
|
-
* Code folding
|
18
|
-
* Selection ranges
|
19
|
-
|
20
|
-
It does not perform typechecking, so its features are implemented on a best-effort basis, aiming to be as accurate as possible.
|
21
|
-
|
22
|
-
Planned future features include:
|
6
|
+
# Ruby LSP
|
23
7
|
|
24
|
-
|
25
|
-
|
8
|
+
The Ruby LSP is an implementation of the [language server protocol](https://microsoft.github.io/language-server-protocol/)
|
9
|
+
for Ruby, used to improve rich features in editors. It is a part of a wider goal to provide a state-of-the-art
|
10
|
+
experience to Ruby developers using modern standards for cross-editor features, documentation and debugging.
|
26
11
|
|
27
|
-
|
12
|
+
Want to discuss Ruby developer experience? Consider joining the public
|
13
|
+
[Ruby DX Slack workspace](https://join.slack.com/t/ruby-dx/shared_invite/zt-1s6f4y15t-v9jedZ9YUPQLM91TEJ4Gew).
|
28
14
|
|
29
|
-
|
15
|
+
## Usage
|
30
16
|
|
31
|
-
|
32
|
-
* Solargraph can be used as a globally installed gem, but Ruby LSP must be added to the Gemfile or gemspec if using RuboCop. (There are pros and cons to each approach)
|
17
|
+
### With VS Code
|
33
18
|
|
34
|
-
|
19
|
+
If using VS Code, all you have to do is install the [Ruby LSP extension](https://github.com/Shopify/vscode-ruby-lsp) to
|
20
|
+
get the extra features in the editor. Do not install this gem manually.
|
35
21
|
|
36
|
-
|
22
|
+
### With other editors
|
37
23
|
|
38
|
-
|
24
|
+
See [editors](https://github.com/Shopify/ruby-lsp/blob/main/EDITORS.md) for community instructions on setting up the
|
25
|
+
Ruby LSP.
|
39
26
|
|
40
|
-
|
27
|
+
The gem can be installed by doing
|
28
|
+
```shell
|
29
|
+
gem install ruby-lsp
|
30
|
+
```
|
41
31
|
|
32
|
+
If you decide to add the gem to the bundle, it is not necessary to require it.
|
42
33
|
```ruby
|
43
34
|
group :development do
|
44
35
|
gem "ruby-lsp", require: false
|
45
36
|
end
|
46
37
|
```
|
47
38
|
|
48
|
-
|
49
|
-
in the editor. See [editors](https://github.com/Shopify/ruby-lsp/blob/main/EDITORS.md) for community instructions on
|
50
|
-
setting up the Ruby LSP in different editors.
|
39
|
+
### Documentation
|
51
40
|
|
52
41
|
See the [documentation](https://shopify.github.io/ruby-lsp) for more in-depth details about the
|
53
42
|
[supported features](https://shopify.github.io/ruby-lsp/RubyLsp/Requests.html).
|
54
43
|
|
44
|
+
## Learn More
|
45
|
+
|
46
|
+
* [RubyConf 2022: Improving the development experience with language servers](https://www.youtube.com/watch?v=kEfXPTm1aCI) ([Vinicius Stock](https://github.com/vinistock))
|
47
|
+
* [Remote Ruby: Ruby Language Server with Vinicius Stock](https://remoteruby.com/221)
|
48
|
+
|
55
49
|
## Contributing
|
56
50
|
|
57
51
|
Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/ruby-lsp.
|
@@ -113,7 +107,7 @@ To add a new expectations test runner for a new request handler:
|
|
113
107
|
* Tests with expectations will be checked with `assert_expectations`
|
114
108
|
* Tests without expectations will be ran against your new $HANDLER to check that nothing breaks
|
115
109
|
|
116
|
-
|
110
|
+
### Debugging
|
117
111
|
|
118
112
|
### Debugging Tests
|
119
113
|
|
@@ -135,7 +129,7 @@ To add a new expectations test runner for a new request handler:
|
|
135
129
|
7. [`ruby-lsp`] Use commands like `b <file>:<line>` or `b Class#method` to set breakpoints and type `c` to continue the process.
|
136
130
|
8. In your `Extension Development Host` project (e.g. [`Tapioca`](https://github.com/Shopify/tapioca)), trigger the request that will hit the breakpoint.
|
137
131
|
|
138
|
-
|
132
|
+
### Spell Checking
|
139
133
|
|
140
134
|
VS Code users will be prompted to enable the [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) extension.
|
141
135
|
By default this will be enabled for all workspaces, but you can choose to selectively enable or disable it per workspace.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.4
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "rubocop"
|
5
|
+
require "sorbet-runtime"
|
6
|
+
|
7
|
+
module RuboCop
|
8
|
+
module Cop
|
9
|
+
module RubyLsp
|
10
|
+
# Prefer using `Interface`, `Transport` and `Constant` aliases
|
11
|
+
# within the `RubyLsp` module, without having to prefix with
|
12
|
+
# `LanguageServer::Protocol`
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# # bad
|
16
|
+
# module RubyLsp
|
17
|
+
# class FoldingRanges
|
18
|
+
# sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::FoldingRange], Object)) }
|
19
|
+
# def run; end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# module RubyLsp
|
24
|
+
# class FoldingRanges
|
25
|
+
# sig { override.returns(T.all(T::Array[Interface::FoldingRange], Object)) }
|
26
|
+
# def run; end
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
class UseLanguageServerAliases < RuboCop::Cop::Base
|
30
|
+
extend RuboCop::Cop::AutoCorrector
|
31
|
+
|
32
|
+
ALIASED_CONSTANTS = T.let([:Interface, :Transport, :Constant].freeze, T::Array[Symbol])
|
33
|
+
|
34
|
+
MSG = "Use constant alias `%{constant}`."
|
35
|
+
|
36
|
+
def_node_search :ruby_lsp_modules, <<~PATTERN
|
37
|
+
(module (const nil? :RubyLsp) ...)
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def_node_search :lsp_constant_usages, <<~PATTERN
|
41
|
+
(const (const (const nil? :LanguageServer) :Protocol) {:Interface | :Transport | :Constant})
|
42
|
+
PATTERN
|
43
|
+
|
44
|
+
def on_new_investigation
|
45
|
+
return if processed_source.blank?
|
46
|
+
|
47
|
+
ruby_lsp_modules(processed_source.ast).each do |ruby_lsp_mod|
|
48
|
+
lsp_constant_usages(ruby_lsp_mod).each do |node|
|
49
|
+
lsp_const = node.children.last
|
50
|
+
|
51
|
+
next unless ALIASED_CONSTANTS.include?(lsp_const)
|
52
|
+
|
53
|
+
add_offense(node, message: format(MSG, constant: lsp_const)) do |corrector|
|
54
|
+
corrector.replace(node, lsp_const)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -15,11 +15,19 @@ module RubyLsp
|
|
15
15
|
sig { returns(String) }
|
16
16
|
attr_reader :source
|
17
17
|
|
18
|
-
sig {
|
19
|
-
|
18
|
+
sig { returns(Integer) }
|
19
|
+
attr_reader :version
|
20
|
+
|
21
|
+
sig { returns(String) }
|
22
|
+
attr_reader :uri
|
23
|
+
|
24
|
+
sig { params(source: String, version: Integer, uri: String, encoding: String).void }
|
25
|
+
def initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind::UTF8)
|
20
26
|
@cache = T.let({}, T::Hash[Symbol, T.untyped])
|
21
27
|
@encoding = T.let(encoding, String)
|
22
28
|
@source = T.let(source, String)
|
29
|
+
@version = T.let(version, Integer)
|
30
|
+
@uri = T.let(uri, String)
|
23
31
|
@unparsed_edits = T.let([], T::Array[EditShape])
|
24
32
|
@syntax_error = T.let(false, T::Boolean)
|
25
33
|
@tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
|
@@ -48,8 +56,8 @@ module RubyLsp
|
|
48
56
|
result
|
49
57
|
end
|
50
58
|
|
51
|
-
sig { params(edits: T::Array[EditShape]).void }
|
52
|
-
def push_edits(edits)
|
59
|
+
sig { params(edits: T::Array[EditShape], version: Integer).void }
|
60
|
+
def push_edits(edits, version:)
|
53
61
|
edits.each do |edit|
|
54
62
|
range = edit[:range]
|
55
63
|
scanner = create_scanner
|
@@ -60,6 +68,7 @@ module RubyLsp
|
|
60
68
|
@source[start_position...end_position] = edit[:text]
|
61
69
|
end
|
62
70
|
|
71
|
+
@version = version
|
63
72
|
@unparsed_edits.concat(edits)
|
64
73
|
@cache.clear
|
65
74
|
end
|
@@ -68,9 +77,9 @@ module RubyLsp
|
|
68
77
|
def parse
|
69
78
|
return if @unparsed_edits.empty?
|
70
79
|
|
80
|
+
@unparsed_edits.clear
|
71
81
|
@tree = SyntaxTree.parse(@source)
|
72
82
|
@syntax_error = false
|
73
|
-
@unparsed_edits.clear
|
74
83
|
rescue SyntaxTree::Parser::ParseError
|
75
84
|
@syntax_error = true
|
76
85
|
end
|
@@ -90,6 +99,61 @@ module RubyLsp
|
|
90
99
|
Scanner.new(@source, @encoding)
|
91
100
|
end
|
92
101
|
|
102
|
+
sig do
|
103
|
+
params(
|
104
|
+
position: PositionShape,
|
105
|
+
node_types: T::Array[T.class_of(SyntaxTree::Node)],
|
106
|
+
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node)])
|
107
|
+
end
|
108
|
+
def locate_node(position, node_types: [])
|
109
|
+
return [nil, nil] unless parsed?
|
110
|
+
|
111
|
+
locate(T.must(@tree), create_scanner.find_char_position(position))
|
112
|
+
end
|
113
|
+
|
114
|
+
sig do
|
115
|
+
params(
|
116
|
+
node: SyntaxTree::Node,
|
117
|
+
char_position: Integer,
|
118
|
+
node_types: T::Array[T.class_of(SyntaxTree::Node)],
|
119
|
+
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node)])
|
120
|
+
end
|
121
|
+
def locate(node, char_position, node_types: [])
|
122
|
+
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(SyntaxTree::Node)])
|
123
|
+
closest = node
|
124
|
+
parent = T.let(nil, T.nilable(SyntaxTree::Node))
|
125
|
+
|
126
|
+
until queue.empty?
|
127
|
+
candidate = queue.shift
|
128
|
+
|
129
|
+
# Skip nil child nodes
|
130
|
+
next if candidate.nil?
|
131
|
+
|
132
|
+
# Add the next child_nodes to the queue to be processed
|
133
|
+
queue.concat(candidate.child_nodes)
|
134
|
+
|
135
|
+
# Skip if the current node doesn't cover the desired position
|
136
|
+
loc = candidate.location
|
137
|
+
next unless (loc.start_char...loc.end_char).cover?(char_position)
|
138
|
+
|
139
|
+
# If the node's start character is already past the position, then we should've found the closest node
|
140
|
+
# already
|
141
|
+
break if char_position < loc.start_char
|
142
|
+
|
143
|
+
# If there are node types to filter by, and the current node is not one of those types, then skip it
|
144
|
+
next if node_types.any? && node_types.none? { |type| candidate.class == type }
|
145
|
+
|
146
|
+
# If the current node is narrower than or equal to the previous closest node, then it is more precise
|
147
|
+
closest_loc = closest.location
|
148
|
+
if loc.end_char - loc.start_char <= closest_loc.end_char - closest_loc.start_char
|
149
|
+
parent = closest
|
150
|
+
closest = candidate
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
[closest, parent]
|
155
|
+
end
|
156
|
+
|
93
157
|
class Scanner
|
94
158
|
extend T::Sig
|
95
159
|
|
@@ -118,7 +182,11 @@ module RubyLsp
|
|
118
182
|
# The final position is the beginning of the line plus the requested column. If the encoding is UTF-16, we also
|
119
183
|
# need to adjust for surrogate pairs
|
120
184
|
requested_position = @pos + position[:character]
|
121
|
-
|
185
|
+
|
186
|
+
if @encoding == Constant::PositionEncodingKind::UTF16
|
187
|
+
requested_position -= utf_16_character_position_correction(@pos, requested_position)
|
188
|
+
end
|
189
|
+
|
122
190
|
requested_position
|
123
191
|
end
|
124
192
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
# EventEmitter is an intermediary between our requests and Syntax Tree visitors. It's used to visit the document's AST
|
6
|
+
# and emit events that the requests can listen to for providing functionality. Usages:
|
7
|
+
#
|
8
|
+
# - For positional requests, locate the target node and use `emit_for_target` to fire events for each listener
|
9
|
+
# - For nonpositional requests, use `visit` to go through the AST, which will fire events for each listener as nodes
|
10
|
+
# are found
|
11
|
+
#
|
12
|
+
# = Example
|
13
|
+
#
|
14
|
+
# ```ruby
|
15
|
+
# target_node = document.locate_node(position)
|
16
|
+
# listener = Requests::Hover.new
|
17
|
+
# EventEmitter.new(listener).emit_for_target(target_node)
|
18
|
+
# listener.response
|
19
|
+
# ```
|
20
|
+
class EventEmitter < SyntaxTree::Visitor
|
21
|
+
extend T::Sig
|
22
|
+
|
23
|
+
sig { params(listeners: Listener[T.untyped]).void }
|
24
|
+
def initialize(*listeners)
|
25
|
+
@listeners = listeners
|
26
|
+
|
27
|
+
# Create a map of event name to listeners that have registered it, so that we avoid unnecessary invocations
|
28
|
+
@event_to_listener_map = T.let(
|
29
|
+
listeners.each_with_object(Hash.new { |h, k| h[k] = [] }) do |listener, hash|
|
30
|
+
listener.class.events&.each { |event| hash[event] << listener }
|
31
|
+
end,
|
32
|
+
T::Hash[Symbol, T::Array[Listener[T.untyped]]],
|
33
|
+
)
|
34
|
+
|
35
|
+
super()
|
36
|
+
end
|
37
|
+
|
38
|
+
# Emit events for a specific node. This is similar to the regular `visit` method, but avoids going deeper into the
|
39
|
+
# tree for performance
|
40
|
+
sig { params(node: T.nilable(SyntaxTree::Node)).void }
|
41
|
+
def emit_for_target(node)
|
42
|
+
case node
|
43
|
+
when SyntaxTree::Command
|
44
|
+
@event_to_listener_map[:on_command]&.each { |listener| T.unsafe(listener).on_command(node) }
|
45
|
+
when SyntaxTree::CallNode
|
46
|
+
@event_to_listener_map[:on_call]&.each { |listener| T.unsafe(listener).on_call(node) }
|
47
|
+
when SyntaxTree::ConstPathRef
|
48
|
+
@event_to_listener_map[:on_const_path_ref]&.each { |listener| T.unsafe(listener).on_const_path_ref(node) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -41,7 +41,11 @@ module RubyLsp
|
|
41
41
|
warn("Ruby LSP is ready")
|
42
42
|
VOID
|
43
43
|
when "textDocument/didOpen"
|
44
|
-
text_document_did_open(
|
44
|
+
text_document_did_open(
|
45
|
+
uri,
|
46
|
+
request.dig(:params, :textDocument, :text),
|
47
|
+
request.dig(:params, :textDocument, :version),
|
48
|
+
)
|
45
49
|
when "textDocument/didClose"
|
46
50
|
@notifications << Notification.new(
|
47
51
|
message: "textDocument/publishDiagnostics",
|
@@ -50,7 +54,11 @@ module RubyLsp
|
|
50
54
|
|
51
55
|
text_document_did_close(uri)
|
52
56
|
when "textDocument/didChange"
|
53
|
-
text_document_did_change(
|
57
|
+
text_document_did_change(
|
58
|
+
uri,
|
59
|
+
request.dig(:params, :contentChanges),
|
60
|
+
request.dig(:params, :textDocument, :version),
|
61
|
+
)
|
54
62
|
when "textDocument/foldingRange"
|
55
63
|
folding_range(uri)
|
56
64
|
when "textDocument/documentLink"
|
@@ -115,6 +123,8 @@ module RubyLsp
|
|
115
123
|
end
|
116
124
|
when "textDocument/completion"
|
117
125
|
completion(uri, request.dig(:params, :position))
|
126
|
+
when "textDocument/codeLens"
|
127
|
+
code_lens(uri)
|
118
128
|
end
|
119
129
|
end
|
120
130
|
|
@@ -125,6 +135,13 @@ module RubyLsp
|
|
125
135
|
end
|
126
136
|
end
|
127
137
|
|
138
|
+
sig { params(uri: String).returns(T::Array[Interface::CodeLens]) }
|
139
|
+
def code_lens(uri)
|
140
|
+
@store.cache_fetch(uri, :code_lens) do |document|
|
141
|
+
Requests::CodeLens.new(document).run
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
128
145
|
sig do
|
129
146
|
params(
|
130
147
|
uri: String,
|
@@ -132,13 +149,26 @@ module RubyLsp
|
|
132
149
|
).returns(T.nilable(Interface::Hover))
|
133
150
|
end
|
134
151
|
def hover(uri, position)
|
135
|
-
|
152
|
+
document = @store.get(uri)
|
153
|
+
document.parse
|
154
|
+
return if document.syntax_error?
|
155
|
+
|
156
|
+
target, parent = document.locate_node(position)
|
157
|
+
|
158
|
+
if !Requests::Hover::ALLOWED_TARGETS.include?(target.class) &&
|
159
|
+
Requests::Hover::ALLOWED_TARGETS.include?(parent.class)
|
160
|
+
target = parent
|
161
|
+
end
|
162
|
+
|
163
|
+
listener = RubyLsp::Requests::Hover.new
|
164
|
+
EventEmitter.new(listener).emit_for_target(target)
|
165
|
+
listener.response
|
136
166
|
end
|
137
167
|
|
138
168
|
sig { params(uri: String).returns(T::Array[Interface::DocumentLink]) }
|
139
169
|
def document_link(uri)
|
140
170
|
@store.cache_fetch(uri, :document_link) do |document|
|
141
|
-
RubyLsp::Requests::DocumentLink.new(
|
171
|
+
RubyLsp::Requests::DocumentLink.new(document).run
|
142
172
|
end
|
143
173
|
end
|
144
174
|
|
@@ -149,15 +179,15 @@ module RubyLsp
|
|
149
179
|
end
|
150
180
|
end
|
151
181
|
|
152
|
-
sig { params(uri: String, content_changes: T::Array[Document::EditShape]).returns(Object) }
|
153
|
-
def text_document_did_change(uri, content_changes)
|
154
|
-
@store.push_edits(uri, content_changes)
|
182
|
+
sig { params(uri: String, content_changes: T::Array[Document::EditShape], version: Integer).returns(Object) }
|
183
|
+
def text_document_did_change(uri, content_changes, version)
|
184
|
+
@store.push_edits(uri: uri, edits: content_changes, version: version)
|
155
185
|
VOID
|
156
186
|
end
|
157
187
|
|
158
|
-
sig { params(uri: String, text: String).returns(Object) }
|
159
|
-
def text_document_did_open(uri, text)
|
160
|
-
@store.set(uri, text)
|
188
|
+
sig { params(uri: String, text: String, version: Integer).returns(Object) }
|
189
|
+
def text_document_did_open(uri, text, version)
|
190
|
+
@store.set(uri: uri, source: text, version: version)
|
161
191
|
VOID
|
162
192
|
end
|
163
193
|
|
@@ -207,7 +237,7 @@ module RubyLsp
|
|
207
237
|
|
208
238
|
sig { params(uri: String).returns(T.nilable(T::Array[Interface::TextEdit])) }
|
209
239
|
def formatting(uri)
|
210
|
-
Requests::Formatting.new(
|
240
|
+
Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).run
|
211
241
|
end
|
212
242
|
|
213
243
|
sig do
|
@@ -250,7 +280,7 @@ module RubyLsp
|
|
250
280
|
def code_action(uri, range, context)
|
251
281
|
document = @store.get(uri)
|
252
282
|
|
253
|
-
Requests::CodeActions.new(
|
283
|
+
Requests::CodeActions.new(document, range, context).run
|
254
284
|
end
|
255
285
|
|
256
286
|
sig { params(params: T::Hash[Symbol, T.untyped]).returns(Interface::CodeAction) }
|
@@ -286,7 +316,7 @@ module RubyLsp
|
|
286
316
|
sig { params(uri: String).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
|
287
317
|
def diagnostic(uri)
|
288
318
|
response = @store.cache_fetch(uri, :diagnostics) do |document|
|
289
|
-
Requests::Diagnostics.new(
|
319
|
+
Requests::Diagnostics.new(document).run
|
290
320
|
end
|
291
321
|
|
292
322
|
Interface::FullDocumentDiagnosticReport.new(kind: "full", items: response.map(&:to_lsp_diagnostic)) if response
|
@@ -318,11 +348,21 @@ module RubyLsp
|
|
318
348
|
sig { params(options: T::Hash[Symbol, T.untyped]).returns(Interface::InitializeResult) }
|
319
349
|
def initialize_request(options)
|
320
350
|
@store.clear
|
321
|
-
|
351
|
+
|
352
|
+
encodings = options.dig(:capabilities, :general, :positionEncodings)
|
353
|
+
@store.encoding = if encodings.nil? || encodings.empty?
|
354
|
+
Constant::PositionEncodingKind::UTF16
|
355
|
+
elsif encodings.include?(Constant::PositionEncodingKind::UTF8)
|
356
|
+
Constant::PositionEncodingKind::UTF8
|
357
|
+
else
|
358
|
+
encodings.first
|
359
|
+
end
|
360
|
+
|
322
361
|
formatter = options.dig(:initializationOptions, :formatter)
|
323
362
|
@store.formatter = formatter unless formatter.nil?
|
324
363
|
|
325
364
|
configured_features = options.dig(:initializationOptions, :enabledFeatures)
|
365
|
+
experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled)
|
326
366
|
|
327
367
|
enabled_features = case configured_features
|
328
368
|
when Array
|
@@ -351,6 +391,10 @@ module RubyLsp
|
|
351
391
|
Interface::DocumentLinkOptions.new(resolve_provider: false)
|
352
392
|
end
|
353
393
|
|
394
|
+
code_lens_provider = if experimental_features
|
395
|
+
Interface::CodeLensOptions.new(resolve_provider: false)
|
396
|
+
end
|
397
|
+
|
354
398
|
hover_provider = if enabled_features["hover"]
|
355
399
|
Interface::HoverClientCapabilities.new(dynamic_registration: false)
|
356
400
|
end
|
@@ -406,6 +450,7 @@ module RubyLsp
|
|
406
450
|
change: Constant::TextDocumentSyncKind::INCREMENTAL,
|
407
451
|
open_close: true,
|
408
452
|
),
|
453
|
+
position_encoding: @store.encoding,
|
409
454
|
selection_range_provider: enabled_features["selectionRanges"],
|
410
455
|
hover_provider: hover_provider,
|
411
456
|
document_symbol_provider: document_symbol_provider,
|
@@ -419,6 +464,7 @@ module RubyLsp
|
|
419
464
|
diagnostic_provider: diagnostics_provider,
|
420
465
|
inlay_hint_provider: inlay_hint_provider,
|
421
466
|
completion_provider: completion_provider,
|
467
|
+
code_lens_provider: code_lens_provider,
|
422
468
|
),
|
423
469
|
)
|
424
470
|
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
# Listener is an abstract class to be used by requests for listening to events emitted when visiting an AST using the
|
6
|
+
# EventEmitter.
|
7
|
+
class Listener
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
extend T::Generic
|
11
|
+
include Requests::Support::Common
|
12
|
+
|
13
|
+
ResponseType = type_member
|
14
|
+
|
15
|
+
abstract!
|
16
|
+
|
17
|
+
class << self
|
18
|
+
extend T::Sig
|
19
|
+
|
20
|
+
sig { returns(T.nilable(T::Array[Symbol])) }
|
21
|
+
attr_reader :events
|
22
|
+
|
23
|
+
# All listener events must be defined inside of a `listener_events` block. This is to ensure we know which events
|
24
|
+
# have been registered. Defining an event outside of this block will simply not register it and it'll never be
|
25
|
+
# invoked
|
26
|
+
sig { params(block: T.proc.void).void }
|
27
|
+
def listener_events(&block)
|
28
|
+
current_methods = instance_methods
|
29
|
+
block.call
|
30
|
+
@events = T.let(instance_methods - current_methods, T.nilable(T::Array[Symbol]))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Override this method with an attr_reader that returns the response of your listener. The listener should
|
35
|
+
# accumulate results in a @response variable and then provide the reader so that it is accessible
|
36
|
+
sig { abstract.returns(ResponseType) }
|
37
|
+
def response; end
|
38
|
+
end
|
39
|
+
end
|