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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62174da68ba92c0af61878221168a02398c9b3df2867b63c063850b1787c7104
4
- data.tar.gz: dc3d2e17e3fe2dcbb6578902eb3b672babc72d0f10511c021db767cfde709b0b
3
+ metadata.gz: 37a06a9c8be918e5c5a22986e68e378fc767f5c5becc46f79baa78d4b00c8c08
4
+ data.tar.gz: 4a79a4a39aaf59c49bddd6420e526246e31ade2a944ab314328f0a30da7661c2
5
5
  SHA512:
6
- metadata.gz: c8295ce6739f9452dd20cab22ebb19fb099040f4782afa4dfd63b423b08ad73eedeb1fccc33113dcd7b27d38745e7112a6d272ea28652e00102a870c18c51b96
7
- data.tar.gz: 1e1a2a681dcdf97bbb1f667211111b89ca274c1d89745bcd3cf8511fbc3d41416d0bb5a45f2fcaf85feb8b0469a5441b5500148136f849657d263531ffe5ff8a
6
+ metadata.gz: 698eaf5cf5d213f6a1ffce60e40f1a4e89d599132e1940699f3a98eb5de7b4141cb442338f97c3a35850a0ed239a52a8d27edaf8a59e7a1f169373a8c2fd9fa1
7
+ data.tar.gz: d5668dfd0c88482fbb6fab215b1782a2fcfc384504a485b8047fd28a99bb3b958de9bde80ecd213b30ae369bfb50d521c60d361e631d06e398f4efb5731e3158
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.3
1
+ 0.3.5
@@ -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
- update_parsable_source(edits)
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
- super(document)
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
- return [] unless @target
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
- node.child_nodes.compact.bsearch do |child|
67
- if (child.location.start_char...child.location.end_char).cover?(position)
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 SyntaxTree::GVar,
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 = node.is_a?(SyntaxTree::Params) ? matched : 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::Array[String]]) }
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::Array[String]]))
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
 
@@ -85,7 +85,7 @@ module RubyLsp
85
85
 
86
86
  sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol], Object)) }
87
87
  def run
88
- visit(@document.tree)
88
+ visit(@document.tree) if @document.parsed?
89
89
  @root.children
90
90
  end
91
91
 
@@ -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
- visit(@document.tree)
70
- emit_partial_range
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
@@ -31,7 +31,7 @@ module RubyLsp
31
31
 
32
32
  sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::InlayHint], Object)) }
33
33
  def run
34
- visit(@document.tree)
34
+ visit(@document.tree) if @document.parsed?
35
35
  @hints
36
36
  end
37
37
 
@@ -70,7 +70,7 @@ module RubyLsp
70
70
 
71
71
  sig { override.returns(T.all(T::Array[Support::SelectionRange], Object)) }
72
72
  def run
73
- visit(@document.tree)
73
+ visit(@document.tree) if @document.parsed?
74
74
  @ranges.reverse!
75
75
  end
76
76
 
@@ -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), :variable)
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, :variable)
180
+ add_token(required.location, :parameter)
178
181
  end
179
182
 
180
183
  rest = node.keyword_rest
181
- return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
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
- name = rest.name
184
- add_token(name.location, :variable) if name
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
- case node.value
201
+ value = node.value
202
+
203
+ case value
190
204
  when SyntaxTree::Ident
191
- add_token(node.value.location, :variable)
205
+ type = type_for_local(value)
206
+ add_token(value.location, type)
192
207
  else
193
- visit(node.value)
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
- case node.value
214
+ value = node.value
215
+
216
+ case value
200
217
  when SyntaxTree::Ident
201
- add_token(node.value.location, :variable)
218
+ type = type_for_local(value)
219
+ add_token(value.location, type)
202
220
  else
203
- visit(node.value)
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
@@ -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
@@ -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
- if document.parsed?
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
- if document.parsed?
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|
@@ -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.nilable(T.type_parameter(:T)))
58
+ ).returns(T.type_parameter(:T))
59
59
  end
60
60
  def cache_fetch(uri, request_name, &block)
61
- document = get(uri)
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.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-03 00:00:00.000000000 Z
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: '3.4'
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: '3.4'
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