ruby-lsp 0.3.3 → 0.3.5

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 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