ruby-lsp 0.4.2 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
![Build Status](https://github.com/Shopify/ruby-lsp/workflows/CI/badge.svg)
|
1
|
+
[![Build Status](https://github.com/Shopify/ruby-lsp/workflows/CI/badge.svg)](https://github.com/Shopify/ruby-lsp/actions/workflows/ci.yml)
|
2
|
+
[![Ruby LSP extension](https://img.shields.io/badge/VS%20Code-Ruby%20LSP-success?logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
|
3
|
+
[![Ruby DX Slack](https://img.shields.io/badge/Slack-Ruby%20DX-success?logo=slack)](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
|