ruby-lsp 0.3.3 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/ruby_lsp/document.rb +11 -3
- data/lib/ruby_lsp/requests/base_request.rb +49 -0
- data/lib/ruby_lsp/requests/document_highlight.rb +19 -21
- data/lib/ruby_lsp/requests/document_link.rb +4 -4
- data/lib/ruby_lsp/requests/document_symbol.rb +1 -1
- data/lib/ruby_lsp/requests/folding_ranges.rb +5 -2
- data/lib/ruby_lsp/requests/hover.rb +72 -0
- data/lib/ruby_lsp/requests/inlay_hints.rb +1 -1
- data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +40 -11
- data/lib/ruby_lsp/requests/support/rails_document_client.rb +114 -0
- data/lib/ruby_lsp/requests.rb +3 -0
- data/lib/ruby_lsp/server.rb +14 -6
- data/lib/ruby_lsp/store.rb +2 -5
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37a06a9c8be918e5c5a22986e68e378fc767f5c5becc46f79baa78d4b00c8c08
|
4
|
+
data.tar.gz: 4a79a4a39aaf59c49bddd6420e526246e31ade2a944ab314328f0a30da7661c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 698eaf5cf5d213f6a1ffce60e40f1a4e89d599132e1940699f3a98eb5de7b4141cb442338f97c3a35850a0ed239a52a8d27edaf8a59e7a1f169373a8c2fd9fa1
|
7
|
+
data.tar.gz: d5668dfd0c88482fbb6fab215b1782a2fcfc384504a485b8047fd28a99bb3b958de9bde80ecd213b30ae369bfb50d521c60d361e631d06e398f4efb5731e3158
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.5
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -24,6 +24,7 @@ module RubyLsp
|
|
24
24
|
@syntax_error_edits = T.let([], T::Array[EditShape])
|
25
25
|
@source = T.let(source, String)
|
26
26
|
@parsable_source = T.let(source.dup, String)
|
27
|
+
@unparsed_edits = T.let([], T::Array[EditShape])
|
27
28
|
@tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
|
28
29
|
rescue SyntaxTree::Parser::ParseError
|
29
30
|
# Do not raise if we failed to parse
|
@@ -55,13 +56,21 @@ module RubyLsp
|
|
55
56
|
# Apply the edits on the real source
|
56
57
|
edits.each { |edit| apply_edit(@source, edit[:range], edit[:text]) }
|
57
58
|
|
59
|
+
@unparsed_edits.concat(edits)
|
58
60
|
@cache.clear
|
61
|
+
end
|
62
|
+
|
63
|
+
sig { void }
|
64
|
+
def parse
|
65
|
+
return if @unparsed_edits.empty?
|
66
|
+
|
59
67
|
@tree = SyntaxTree.parse(@source)
|
60
68
|
@syntax_error_edits.clear
|
69
|
+
@unparsed_edits.clear
|
61
70
|
@parsable_source = @source.dup
|
62
|
-
nil
|
63
71
|
rescue SyntaxTree::Parser::ParseError
|
64
|
-
|
72
|
+
@syntax_error_edits = @unparsed_edits
|
73
|
+
update_parsable_source(@unparsed_edits)
|
65
74
|
end
|
66
75
|
|
67
76
|
sig { returns(T::Boolean) }
|
@@ -81,7 +90,6 @@ module RubyLsp
|
|
81
90
|
# If the new edits caused a syntax error, make all edits blank spaces and line breaks to adjust the line and
|
82
91
|
# column numbers. This is attempt to make the document parsable while partial edits are being applied
|
83
92
|
edits.each do |edit|
|
84
|
-
@syntax_error_edits << edit
|
85
93
|
next if edit[:text].empty? # skip deletions, since they may have caused the syntax error
|
86
94
|
|
87
95
|
apply_edit(@parsable_source, edit[:range], edit[:text].gsub(/[^\r\n]/, " "))
|
@@ -14,6 +14,10 @@ module RubyLsp
|
|
14
14
|
def initialize(document)
|
15
15
|
@document = document
|
16
16
|
|
17
|
+
# Parsing the document here means we're taking a lazy approach by only doing it when the first feature request
|
18
|
+
# is received by the server. This happens because {Document#parse} remembers if there are new edits to be parsed
|
19
|
+
@document.parse
|
20
|
+
|
17
21
|
super()
|
18
22
|
end
|
19
23
|
|
@@ -30,6 +34,51 @@ module RubyLsp
|
|
30
34
|
end: LanguageServer::Protocol::Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
|
31
35
|
)
|
32
36
|
end
|
37
|
+
|
38
|
+
sig { params(node: SyntaxTree::ConstPathRef).returns(String) }
|
39
|
+
def full_constant_name(node)
|
40
|
+
name = +node.constant.value
|
41
|
+
constant = T.let(node, SyntaxTree::Node)
|
42
|
+
|
43
|
+
while constant.is_a?(SyntaxTree::ConstPathRef)
|
44
|
+
constant = constant.parent
|
45
|
+
|
46
|
+
case constant
|
47
|
+
when SyntaxTree::ConstPathRef
|
48
|
+
name.prepend("#{constant.constant.value}::")
|
49
|
+
when SyntaxTree::VarRef
|
50
|
+
name.prepend("#{constant.value.value}::")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
name
|
55
|
+
end
|
56
|
+
|
57
|
+
sig do
|
58
|
+
params(
|
59
|
+
parent: SyntaxTree::Node,
|
60
|
+
target_nodes: T::Array[T.class_of(SyntaxTree::Node)],
|
61
|
+
position: Integer,
|
62
|
+
).returns(T::Array[SyntaxTree::Node])
|
63
|
+
end
|
64
|
+
def locate_node_and_parent(parent, target_nodes, position)
|
65
|
+
matched = parent.child_nodes.compact.bsearch do |child|
|
66
|
+
if (child.location.start_char...child.location.end_char).cover?(position)
|
67
|
+
0
|
68
|
+
else
|
69
|
+
position <=> child.location.start_char
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
case matched
|
74
|
+
when *target_nodes
|
75
|
+
[matched, parent]
|
76
|
+
when SyntaxTree::Node
|
77
|
+
locate_node_and_parent(matched, target_nodes, position)
|
78
|
+
else
|
79
|
+
[]
|
80
|
+
end
|
81
|
+
end
|
33
82
|
end
|
34
83
|
end
|
35
84
|
end
|
@@ -27,19 +27,20 @@ module RubyLsp
|
|
27
27
|
|
28
28
|
sig { params(document: Document, position: Document::PositionShape).void }
|
29
29
|
def initialize(document, position)
|
30
|
+
super(document)
|
31
|
+
|
30
32
|
@highlights = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
|
31
33
|
position = Document::Scanner.new(document.source).find_position(position)
|
32
|
-
@target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
|
33
34
|
|
34
|
-
|
35
|
+
return unless document.parsed?
|
36
|
+
|
37
|
+
@target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
|
35
38
|
end
|
36
39
|
|
37
40
|
sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentHighlight], Object)) }
|
38
41
|
def run
|
39
42
|
# no @target means the target is not highlightable
|
40
|
-
|
41
|
-
|
42
|
-
visit(@document.tree)
|
43
|
+
visit(@document.tree) if @document.parsed? && @target
|
43
44
|
@highlights
|
44
45
|
end
|
45
46
|
|
@@ -55,6 +56,14 @@ module RubyLsp
|
|
55
56
|
|
56
57
|
private
|
57
58
|
|
59
|
+
DIRECT_HIGHLIGHTS = T.let([
|
60
|
+
SyntaxTree::GVar,
|
61
|
+
SyntaxTree::IVar,
|
62
|
+
SyntaxTree::Const,
|
63
|
+
SyntaxTree::CVar,
|
64
|
+
SyntaxTree::VarField,
|
65
|
+
], T::Array[T.class_of(SyntaxTree::Node)])
|
66
|
+
|
58
67
|
sig do
|
59
68
|
params(
|
60
69
|
node: SyntaxTree::Node,
|
@@ -62,27 +71,16 @@ module RubyLsp
|
|
62
71
|
).returns(T.nilable(Support::HighlightTarget))
|
63
72
|
end
|
64
73
|
def find(node, position)
|
65
|
-
matched =
|
66
|
-
|
67
|
-
|
68
|
-
0
|
69
|
-
else
|
70
|
-
position <=> child.location.start_char
|
71
|
-
end
|
72
|
-
end
|
74
|
+
matched, parent = locate_node_and_parent(node, DIRECT_HIGHLIGHTS + [SyntaxTree::Ident], position)
|
75
|
+
|
76
|
+
return unless matched && parent
|
73
77
|
|
74
78
|
case matched
|
75
|
-
when
|
76
|
-
SyntaxTree::IVar,
|
77
|
-
SyntaxTree::Const,
|
78
|
-
SyntaxTree::CVar,
|
79
|
-
SyntaxTree::VarField
|
79
|
+
when *DIRECT_HIGHLIGHTS
|
80
80
|
Support::HighlightTarget.new(matched)
|
81
81
|
when SyntaxTree::Ident
|
82
|
-
relevant_node =
|
82
|
+
relevant_node = parent.is_a?(SyntaxTree::Params) ? matched : parent
|
83
83
|
Support::HighlightTarget.new(relevant_node)
|
84
|
-
when SyntaxTree::Node
|
85
|
-
find(matched, position)
|
86
84
|
end
|
87
85
|
end
|
88
86
|
|
@@ -31,7 +31,7 @@ module RubyLsp
|
|
31
31
|
class << self
|
32
32
|
extend T::Sig
|
33
33
|
|
34
|
-
sig { returns(T::Hash[String, T::
|
34
|
+
sig { returns(T::Hash[String, T::Hash[String, T::Hash[String, String]]]) }
|
35
35
|
def gem_paths
|
36
36
|
@gem_paths ||= T.let(begin
|
37
37
|
lookup = {}
|
@@ -61,7 +61,7 @@ module RubyLsp
|
|
61
61
|
end
|
62
62
|
|
63
63
|
lookup
|
64
|
-
end, T.nilable(T::Hash[String, T::
|
64
|
+
end, T.nilable(T::Hash[String, T::Hash[String, T::Hash[String, String]]]))
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -78,7 +78,7 @@ module RubyLsp
|
|
78
78
|
|
79
79
|
sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentLink], Object)) }
|
80
80
|
def run
|
81
|
-
visit(@document.tree)
|
81
|
+
visit(@document.tree) if @document.parsed?
|
82
82
|
@links
|
83
83
|
end
|
84
84
|
|
@@ -88,7 +88,7 @@ module RubyLsp
|
|
88
88
|
return unless match
|
89
89
|
|
90
90
|
uri = T.cast(URI(match[0]), URI::Source)
|
91
|
-
gem_version = resolve_version(uri)
|
91
|
+
gem_version = T.must(resolve_version(uri))
|
92
92
|
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, uri.path)
|
93
93
|
return if file_path.nil?
|
94
94
|
|
@@ -66,8 +66,11 @@ module RubyLsp
|
|
66
66
|
|
67
67
|
sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::FoldingRange], Object)) }
|
68
68
|
def run
|
69
|
-
|
70
|
-
|
69
|
+
if @document.parsed?
|
70
|
+
visit(@document.tree)
|
71
|
+
emit_partial_range
|
72
|
+
end
|
73
|
+
|
71
74
|
@ranges
|
72
75
|
end
|
73
76
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# ![Hover demo](../../misc/rails_document_link_hover.gif)
|
7
|
+
#
|
8
|
+
# The [hover request](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
|
9
|
+
# renders a clickable link to the code's official documentation.
|
10
|
+
# It currently only supports Rails' documentation: when hovering over Rails DSLs/constants under certain paths,
|
11
|
+
# like `before_save :callback` in `models/post.rb`, it generates a link to `before_save`'s API documentation.
|
12
|
+
#
|
13
|
+
# # Example
|
14
|
+
#
|
15
|
+
# ```ruby
|
16
|
+
# class Post < ApplicationRecord
|
17
|
+
# before_save :do_something # when hovering on before_save, the link will be rendered
|
18
|
+
# end
|
19
|
+
# ```
|
20
|
+
class Hover < BaseRequest
|
21
|
+
extend T::Sig
|
22
|
+
|
23
|
+
sig { params(document: Document, position: Document::PositionShape).void }
|
24
|
+
def initialize(document, position)
|
25
|
+
super(document)
|
26
|
+
|
27
|
+
@position = T.let(Document::Scanner.new(document.source).find_position(position), Integer)
|
28
|
+
end
|
29
|
+
|
30
|
+
sig { override.returns(T.nilable(LanguageServer::Protocol::Interface::Hover)) }
|
31
|
+
def run
|
32
|
+
return unless @document.parsed?
|
33
|
+
|
34
|
+
target, _ = locate_node_and_parent(
|
35
|
+
T.must(@document.tree), [SyntaxTree::Command, SyntaxTree::FCall, SyntaxTree::ConstPathRef], @position
|
36
|
+
)
|
37
|
+
|
38
|
+
case target
|
39
|
+
when SyntaxTree::Command
|
40
|
+
message = target.message
|
41
|
+
generate_rails_document_link_hover(message.value, message)
|
42
|
+
when SyntaxTree::FCall
|
43
|
+
message = target.value
|
44
|
+
generate_rails_document_link_hover(message.value, message)
|
45
|
+
when SyntaxTree::ConstPathRef
|
46
|
+
constant_name = full_constant_name(target)
|
47
|
+
generate_rails_document_link_hover(constant_name, target)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
sig do
|
54
|
+
params(name: String, node: SyntaxTree::Node).returns(T.nilable(LanguageServer::Protocol::Interface::Hover))
|
55
|
+
end
|
56
|
+
def generate_rails_document_link_hover(name, node)
|
57
|
+
urls = Support::RailsDocumentClient.generate_rails_document_urls(name)
|
58
|
+
|
59
|
+
return if urls.empty?
|
60
|
+
|
61
|
+
contents = LanguageServer::Protocol::Interface::MarkupContent.new(
|
62
|
+
kind: "markdown",
|
63
|
+
value: urls.join("\n\n"),
|
64
|
+
)
|
65
|
+
LanguageServer::Protocol::Interface::Hover.new(
|
66
|
+
range: range_from_syntax_tree_node(node),
|
67
|
+
contents: contents,
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -20,6 +20,7 @@ module RubyLsp
|
|
20
20
|
# ```
|
21
21
|
class SemanticHighlighting < BaseRequest
|
22
22
|
extend T::Sig
|
23
|
+
include SyntaxTree::WithEnvironment
|
23
24
|
|
24
25
|
TOKEN_TYPES = T.let({
|
25
26
|
namespace: 0,
|
@@ -94,6 +95,8 @@ module RubyLsp
|
|
94
95
|
)
|
95
96
|
end
|
96
97
|
def run
|
98
|
+
return @tokens unless @document.parsed?
|
99
|
+
|
97
100
|
visit(@tree)
|
98
101
|
return @tokens unless @encoder
|
99
102
|
|
@@ -170,37 +173,52 @@ module RubyLsp
|
|
170
173
|
def visit_params(node)
|
171
174
|
node.keywords.each do |keyword,|
|
172
175
|
location = keyword.location
|
173
|
-
add_token(location_without_colon(location), :
|
176
|
+
add_token(location_without_colon(location), :parameter)
|
174
177
|
end
|
175
178
|
|
176
179
|
node.requireds.each do |required|
|
177
|
-
add_token(required.location, :
|
180
|
+
add_token(required.location, :parameter)
|
178
181
|
end
|
179
182
|
|
180
183
|
rest = node.keyword_rest
|
181
|
-
|
184
|
+
if rest && !rest.is_a?(SyntaxTree::ArgsForward)
|
185
|
+
name = rest.name
|
186
|
+
add_token(name.location, :parameter) if name
|
187
|
+
end
|
182
188
|
|
183
|
-
|
184
|
-
|
189
|
+
super
|
190
|
+
end
|
191
|
+
|
192
|
+
sig { override.params(node: SyntaxTree::Field).void }
|
193
|
+
def visit_field(node)
|
194
|
+
add_token(node.name.location, :method)
|
195
|
+
|
196
|
+
super
|
185
197
|
end
|
186
198
|
|
187
199
|
sig { override.params(node: SyntaxTree::VarField).void }
|
188
200
|
def visit_var_field(node)
|
189
|
-
|
201
|
+
value = node.value
|
202
|
+
|
203
|
+
case value
|
190
204
|
when SyntaxTree::Ident
|
191
|
-
|
205
|
+
type = type_for_local(value)
|
206
|
+
add_token(value.location, type)
|
192
207
|
else
|
193
|
-
visit(
|
208
|
+
visit(value)
|
194
209
|
end
|
195
210
|
end
|
196
211
|
|
197
212
|
sig { override.params(node: SyntaxTree::VarRef).void }
|
198
213
|
def visit_var_ref(node)
|
199
|
-
|
214
|
+
value = node.value
|
215
|
+
|
216
|
+
case value
|
200
217
|
when SyntaxTree::Ident
|
201
|
-
|
218
|
+
type = type_for_local(value)
|
219
|
+
add_token(value.location, type)
|
202
220
|
else
|
203
|
-
visit(
|
221
|
+
visit(value)
|
204
222
|
end
|
205
223
|
end
|
206
224
|
|
@@ -261,6 +279,17 @@ module RubyLsp
|
|
261
279
|
def special_method?(method_name)
|
262
280
|
SPECIAL_RUBY_METHODS.include?(method_name)
|
263
281
|
end
|
282
|
+
|
283
|
+
sig { params(value: SyntaxTree::Ident).returns(Symbol) }
|
284
|
+
def type_for_local(value)
|
285
|
+
local = current_environment.find_local(value.value)
|
286
|
+
|
287
|
+
if local.nil? || local.type == :variable
|
288
|
+
:variable
|
289
|
+
else
|
290
|
+
:parameter
|
291
|
+
end
|
292
|
+
end
|
264
293
|
end
|
265
294
|
end
|
266
295
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "net/http"
|
5
|
+
|
6
|
+
module RubyLsp
|
7
|
+
module Requests
|
8
|
+
module Support
|
9
|
+
class RailsDocumentClient
|
10
|
+
RAILS_DOC_HOST = "https://api.rubyonrails.org"
|
11
|
+
SUPPORTED_RAILS_DOC_NAMESPACES = T.let(
|
12
|
+
Regexp.union(
|
13
|
+
/ActionDispatch/, /ActionController/, /AbstractController/, /ActiveRecord/, /ActiveModel/, /ActiveStorage/,
|
14
|
+
/ActionText/, /ActiveJob/
|
15
|
+
).freeze,
|
16
|
+
Regexp,
|
17
|
+
)
|
18
|
+
|
19
|
+
RAILTIES_VERSION = T.let(
|
20
|
+
[*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].find do |s|
|
21
|
+
s.name == "railties"
|
22
|
+
end&.version&.to_s, T.nilable(String)
|
23
|
+
)
|
24
|
+
|
25
|
+
class << self
|
26
|
+
extend T::Sig
|
27
|
+
sig do
|
28
|
+
params(name: String).returns(T::Array[String])
|
29
|
+
end
|
30
|
+
def generate_rails_document_urls(name)
|
31
|
+
docs = search_index&.fetch(name, nil)
|
32
|
+
|
33
|
+
return [] unless docs
|
34
|
+
|
35
|
+
docs.map do |doc|
|
36
|
+
owner = doc[:owner]
|
37
|
+
|
38
|
+
link_name =
|
39
|
+
# class/module name
|
40
|
+
if owner == name
|
41
|
+
name
|
42
|
+
else
|
43
|
+
"#{owner}##{name}"
|
44
|
+
end
|
45
|
+
|
46
|
+
"[Rails Document: `#{link_name}`](#{doc[:url]})"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
sig { returns(T.nilable(T::Hash[String, T::Array[T::Hash[Symbol, String]]])) }
|
51
|
+
private def search_index
|
52
|
+
@rails_documents ||= T.let(
|
53
|
+
build_search_index,
|
54
|
+
T.nilable(T::Hash[String, T::Array[T::Hash[Symbol, String]]]),
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { returns(T.nilable(T::Hash[String, T::Array[T::Hash[Symbol, String]]])) }
|
59
|
+
private def build_search_index
|
60
|
+
return unless RAILTIES_VERSION
|
61
|
+
|
62
|
+
$stderr.puts "Fetching Rails Documents..."
|
63
|
+
# If the version's doc is not found, e.g. Rails main, it'll be redirected
|
64
|
+
# In this case, we just fetch the latest doc
|
65
|
+
response = if Gem::Version.new(RAILTIES_VERSION).prerelease?
|
66
|
+
Net::HTTP.get_response(URI("#{RAILS_DOC_HOST}/js/search_index.js"))
|
67
|
+
else
|
68
|
+
Net::HTTP.get_response(URI("#{RAILS_DOC_HOST}/v#{RAILTIES_VERSION}/js/search_index.js"))
|
69
|
+
end
|
70
|
+
|
71
|
+
if response.code == "200"
|
72
|
+
process_search_index(response.body)
|
73
|
+
else
|
74
|
+
$stderr.puts("Response failed: #{response.inspect}")
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
rescue StandardError => e
|
78
|
+
$stderr.puts("Exception occurred when fetching Rails document index: #{e.inspect}")
|
79
|
+
end
|
80
|
+
|
81
|
+
sig { params(js: String).returns(T::Hash[String, T::Array[T::Hash[Symbol, String]]]) }
|
82
|
+
private def process_search_index(js)
|
83
|
+
raw_data = js.sub("var search_data = ", "")
|
84
|
+
info = JSON.parse(raw_data).dig("index", "info")
|
85
|
+
|
86
|
+
# An entry looks like this:
|
87
|
+
#
|
88
|
+
# ["belongs_to", # method or module/class
|
89
|
+
# "ActiveRecord::Associations::ClassMethods", # method owner
|
90
|
+
# "classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to", # path to the document
|
91
|
+
# "(name, scope = nil, **options)", # method's parameters
|
92
|
+
# "<p>Specifies a one-to-one association with another class..."] # document preview
|
93
|
+
#
|
94
|
+
info.each_with_object({}) do |(method_or_class, method_owner, doc_path, _, doc_preview), table|
|
95
|
+
# If a method doesn't have documentation, there's no need to generate the link to it.
|
96
|
+
next if doc_preview.nil? || doc_preview.empty?
|
97
|
+
|
98
|
+
# If the method or class/module is not from the supported namespace, reject it
|
99
|
+
next unless [method_or_class, method_owner].any? do |elem|
|
100
|
+
elem.match?(SUPPORTED_RAILS_DOC_NAMESPACES)
|
101
|
+
end
|
102
|
+
|
103
|
+
owner = method_owner.empty? ? method_or_class : method_owner
|
104
|
+
table[method_or_class] ||= []
|
105
|
+
# It's possible to have multiple modules defining the same method name. For example,
|
106
|
+
# both `ActiveRecord::FinderMethods` and `ActiveRecord::Associations::CollectionProxy` defines `#find`
|
107
|
+
table[method_or_class] << { owner: owner, url: "#{RAILS_DOC_HOST}/v#{RAILTIES_VERSION}/#{doc_path}" }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -6,6 +6,7 @@ module RubyLsp
|
|
6
6
|
#
|
7
7
|
# - {RubyLsp::Requests::DocumentSymbol}
|
8
8
|
# - {RubyLsp::Requests::DocumentLink}
|
9
|
+
# - {RubyLsp::Requests::Hover}
|
9
10
|
# - {RubyLsp::Requests::FoldingRanges}
|
10
11
|
# - {RubyLsp::Requests::SelectionRanges}
|
11
12
|
# - {RubyLsp::Requests::SemanticHighlighting}
|
@@ -20,6 +21,7 @@ module RubyLsp
|
|
20
21
|
autoload :BaseRequest, "ruby_lsp/requests/base_request"
|
21
22
|
autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
|
22
23
|
autoload :DocumentLink, "ruby_lsp/requests/document_link"
|
24
|
+
autoload :Hover, "ruby_lsp/requests/hover"
|
23
25
|
autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges"
|
24
26
|
autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
|
25
27
|
autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
|
@@ -37,6 +39,7 @@ module RubyLsp
|
|
37
39
|
autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
|
38
40
|
autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic"
|
39
41
|
autoload :HighlightTarget, "ruby_lsp/requests/support/highlight_target"
|
42
|
+
autoload :RailsDocumentClient, "ruby_lsp/requests/support/rails_document_client"
|
40
43
|
end
|
41
44
|
end
|
42
45
|
end
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -23,6 +23,10 @@ module RubyLsp
|
|
23
23
|
Interface::DocumentLinkOptions.new(resolve_provider: false)
|
24
24
|
end
|
25
25
|
|
26
|
+
hover_provider = if enabled_features.include?("hover")
|
27
|
+
Interface::HoverClientCapabilities.new(dynamic_registration: false)
|
28
|
+
end
|
29
|
+
|
26
30
|
folding_ranges_provider = if enabled_features.include?("foldingRanges")
|
27
31
|
Interface::FoldingRangeClientCapabilities.new(line_folding_only: true)
|
28
32
|
end
|
@@ -66,6 +70,7 @@ module RubyLsp
|
|
66
70
|
open_close: true,
|
67
71
|
),
|
68
72
|
selection_range_provider: enabled_features.include?("selectionRanges"),
|
73
|
+
hover_provider: hover_provider,
|
69
74
|
document_symbol_provider: document_symbol_provider,
|
70
75
|
document_link_provider: document_link_provider,
|
71
76
|
folding_range_provider: folding_ranges_provider,
|
@@ -116,6 +121,13 @@ module RubyLsp
|
|
116
121
|
end
|
117
122
|
end
|
118
123
|
|
124
|
+
on("textDocument/hover") do |request|
|
125
|
+
position = request.dig(:params, :position)
|
126
|
+
document = store.get(request.dig(:params, :textDocument, :uri))
|
127
|
+
|
128
|
+
RubyLsp::Requests::Hover.new(document, position).run
|
129
|
+
end
|
130
|
+
|
119
131
|
on("textDocument/foldingRange", parallel: true) do |request|
|
120
132
|
store.cache_fetch(request.dig(:params, :textDocument, :uri), :folding_ranges) do |document|
|
121
133
|
Requests::FoldingRanges.new(document).run
|
@@ -175,9 +187,7 @@ module RubyLsp
|
|
175
187
|
on("textDocument/documentHighlight", parallel: true) do |request|
|
176
188
|
document = store.get(request.dig(:params, :textDocument, :uri))
|
177
189
|
|
178
|
-
|
179
|
-
Requests::DocumentHighlight.new(document, request.dig(:params, :position)).run
|
180
|
-
end
|
190
|
+
Requests::DocumentHighlight.new(document, request.dig(:params, :position)).run
|
181
191
|
end
|
182
192
|
|
183
193
|
on("textDocument/codeAction", parallel: true) do |request|
|
@@ -197,9 +207,7 @@ module RubyLsp
|
|
197
207
|
start_line = range.dig(:start, :line)
|
198
208
|
end_line = range.dig(:end, :line)
|
199
209
|
|
200
|
-
|
201
|
-
Requests::InlayHints.new(document, start_line..end_line).run
|
202
|
-
end
|
210
|
+
Requests::InlayHints.new(document, start_line..end_line).run
|
203
211
|
end
|
204
212
|
|
205
213
|
on("$/cancelRequest") do |request|
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -55,13 +55,10 @@ module RubyLsp
|
|
55
55
|
uri: String,
|
56
56
|
request_name: Symbol,
|
57
57
|
block: T.proc.params(document: Document).returns(T.type_parameter(:T)),
|
58
|
-
).returns(T.
|
58
|
+
).returns(T.type_parameter(:T))
|
59
59
|
end
|
60
60
|
def cache_fetch(uri, request_name, &block)
|
61
|
-
|
62
|
-
return unless document.parsed?
|
63
|
-
|
64
|
-
document.cache_fetch(request_name, &block)
|
61
|
+
get(uri).cache_fetch(request_name, &block)
|
65
62
|
end
|
66
63
|
end
|
67
64
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 4.0.2
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 4.0.2
|
55
55
|
description: An opinionated language server for Ruby
|
56
56
|
email:
|
57
57
|
- ruby@shopify.com
|
@@ -78,11 +78,13 @@ files:
|
|
78
78
|
- lib/ruby_lsp/requests/document_symbol.rb
|
79
79
|
- lib/ruby_lsp/requests/folding_ranges.rb
|
80
80
|
- lib/ruby_lsp/requests/formatting.rb
|
81
|
+
- lib/ruby_lsp/requests/hover.rb
|
81
82
|
- lib/ruby_lsp/requests/inlay_hints.rb
|
82
83
|
- lib/ruby_lsp/requests/on_type_formatting.rb
|
83
84
|
- lib/ruby_lsp/requests/selection_ranges.rb
|
84
85
|
- lib/ruby_lsp/requests/semantic_highlighting.rb
|
85
86
|
- lib/ruby_lsp/requests/support/highlight_target.rb
|
87
|
+
- lib/ruby_lsp/requests/support/rails_document_client.rb
|
86
88
|
- lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
|
87
89
|
- lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb
|
88
90
|
- lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb
|