ruby-lsp 0.6.1 → 0.6.2

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: 11dd3401d408ed456da9b4dd0aca906b991a45a0103ca8786eafbf34d4659c85
4
- data.tar.gz: 8469dd62a3660965daee589835e1481fe09e17b55cbd9d61c461d4ba94db833e
3
+ metadata.gz: 0da1b154c2c048cdea8d1ca6f87e77b895482cdbea155058550eff983fdbae1c
4
+ data.tar.gz: 7a67502b28e7e525acc6349fafe5de9e9945350d84af72d0a9afaa5d5b0803d8
5
5
  SHA512:
6
- metadata.gz: 6d2e0c3d9c5bf11068b1d5e3092d4b5f3b233066a33b7e74ea218f115fbb8b154798a8b8b0634569781245f1dedcb6da3e5ca3a23db04f78fc105c496d2a3280
7
- data.tar.gz: 529ce841833605ac40c67536bbca1eb1a9568b742de854f4698ced147813ec63569fd926d4035d518f7bb0ab54d2bbd4665e7ad2564923b49036573a0d4625e7
6
+ metadata.gz: 77968226990bdcbab7026e962a3b7e1e3459f920939aa19a50331daa7b4a24b180eb46a342b5385412a43b73d1d6e40d6f9de2f1321a512cf65d36046b978cbc
7
+ data.tar.gz: a27e182f7c554d9b231f4ea69b15b52d0c4c32fe0cbb17879480c8b9ada87288c59ffa93fbfd99963c456bf4a6298164c920f720611532609f5e985e3c1e890c
data/README.md CHANGED
@@ -42,6 +42,15 @@ See the [documentation](https://shopify.github.io/ruby-lsp) for more in-depth de
42
42
  For creating rich themes for Ruby using the semantic highlighting information, see the [semantic highlighting
43
43
  documentation](SEMANTIC_HIGHLIGHTING.md).
44
44
 
45
+ ### Extensions
46
+
47
+ The Ruby LSP provides a server extension system that allows other gems to enhance the base functionality with more
48
+ editor features. This is the mechanism that powers extensions like
49
+
50
+ - [Ruby LSP Rails](https://github.com/Shopify/ruby-lsp-rails)
51
+
52
+ For instructions on how to create extensions, see the [server extensions documentation](SERVER_EXTENSIONS.md).
53
+
45
54
  ## Learn More
46
55
 
47
56
  * [RubyConf 2022: Improving the development experience with language servers](https://www.youtube.com/watch?v=kEfXPTm1aCI) ([Vinicius Stock](https://github.com/vinistock))
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.1
1
+ 0.6.2
@@ -52,7 +52,7 @@ module RubyLsp
52
52
  # Find all classes that inherit from BaseRequest or Listener, which are the ones we want to make sure are
53
53
  # documented
54
54
  features = ObjectSpace.each_object(Class).filter_map do |k|
55
- klass = T.cast(k, T::Class[T.anything])
55
+ klass = T.unsafe(k)
56
56
  klass if klass < RubyLsp::Requests::BaseRequest || klass < RubyLsp::Listener
57
57
  end
58
58
 
@@ -53,6 +53,12 @@ module RubyLsp
53
53
 
54
54
  # Visit dispatchers are below. Notice that for nodes that create a new scope (e.g.: classes, modules, method defs)
55
55
  # we need both an `on_*` and `after_*` event. This is because some requests must know when we exit the scope
56
+ sig { override.params(node: T.nilable(SyntaxTree::Node)).void }
57
+ def visit(node)
58
+ @listeners[:on_node]&.each { |l| T.unsafe(l).on_node(node) }
59
+ super
60
+ end
61
+
56
62
  sig { override.params(node: SyntaxTree::ClassDeclaration).void }
57
63
  def visit_class(node)
58
64
  @listeners[:on_class]&.each { |l| T.unsafe(l).on_class(node) }
@@ -130,7 +130,7 @@ module RubyLsp
130
130
  )
131
131
 
132
132
  nil
133
- rescue StandardError => error
133
+ rescue StandardError, LoadError => error
134
134
  @message_queue << Notification.new(
135
135
  message: "window/showMessage",
136
136
  params: Interface::ShowMessageParams.new(
@@ -156,7 +156,7 @@ module RubyLsp
156
156
  when "textDocument/diagnostic"
157
157
  begin
158
158
  diagnostic(uri)
159
- rescue StandardError => error
159
+ rescue StandardError, LoadError => error
160
160
  @message_queue << Notification.new(
161
161
  message: "window/showMessage",
162
162
  params: Interface::ShowMessageParams.new(
@@ -275,10 +275,17 @@ module RubyLsp
275
275
  params(
276
276
  uri: String,
277
277
  position: Document::PositionShape,
278
- ).returns(T::Array[Interface::DocumentHighlight])
278
+ ).returns(T.nilable(T::Array[Interface::DocumentHighlight]))
279
279
  end
280
280
  def document_highlight(uri, position)
281
- Requests::DocumentHighlight.new(@store.get(uri), position).run
281
+ document = @store.get(uri)
282
+ return if document.syntax_error?
283
+
284
+ target, parent = document.locate_node(position)
285
+ emitter = EventEmitter.new
286
+ listener = Requests::DocumentHighlight.new(target, parent, emitter, @message_queue)
287
+ emitter.visit(document.tree)
288
+ listener.response
282
289
  end
283
290
 
284
291
  sig { params(uri: String, range: Document::RangeShape).returns(T.nilable(T::Array[Interface::InlayHint])) }
@@ -29,6 +29,7 @@ module RubyLsp
29
29
 
30
30
  BASE_COMMAND = T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String)
31
31
  ACCESS_MODIFIERS = T.let(["public", "private", "protected"], T::Array[String])
32
+ SUPPORTED_TEST_LIBRARIES = T.let(["minitest", "test-unit"], T::Array[String])
32
33
 
33
34
  sig { override.returns(ResponseType) }
34
35
  attr_reader :response
@@ -61,8 +62,9 @@ module RubyLsp
61
62
  def on_class(node)
62
63
  @visibility_stack.push(["public", "public"])
63
64
  class_name = node.constant.constant.value
65
+ @class_stack.push(class_name)
66
+
64
67
  if class_name.end_with?("Test")
65
- @class_stack.push(class_name)
66
68
  add_test_code_lens(
67
69
  node,
68
70
  name: class_name,
@@ -81,7 +83,7 @@ module RubyLsp
81
83
  sig { params(node: SyntaxTree::DefNode).void }
82
84
  def on_def(node)
83
85
  class_name = @class_stack.last
84
- return unless class_name
86
+ return unless class_name&.end_with?("Test")
85
87
 
86
88
  visibility, _ = @visibility_stack.last
87
89
  if visibility == "public"
@@ -156,6 +158,9 @@ module RubyLsp
156
158
 
157
159
  sig { params(node: SyntaxTree::Node, name: String, command: String, kind: Symbol).void }
158
160
  def add_test_code_lens(node, name:, command:, kind:)
161
+ # don't add code lenses if the test library is not supported or unknown
162
+ return unless SUPPORTED_TEST_LIBRARIES.include?(@test_library)
163
+
159
164
  arguments = [
160
165
  @path,
161
166
  name,
@@ -195,10 +200,13 @@ module RubyLsp
195
200
 
196
201
  sig { params(node: SyntaxTree::Command).returns(T.nilable(String)) }
197
202
  def resolve_gem_remote(node)
198
- gem_statement = node.arguments.parts.flat_map(&:child_nodes).first
199
- return unless gem_statement
203
+ gem_statement = node.arguments.parts.first
204
+ return unless gem_statement.is_a?(SyntaxTree::StringLiteral)
205
+
206
+ gem_name = gem_statement.parts.first
207
+ return unless gem_name.is_a?(SyntaxTree::TStringContent)
200
208
 
201
- spec = Gem::Specification.stubs.find { |gem| gem.name == gem_statement.value }&.to_spec
209
+ spec = Gem::Specification.stubs.find { |gem| gem.name == gem_name.value }&.to_spec
202
210
  return if spec.nil?
203
211
 
204
212
  [spec.homepage, spec.metadata["source_code_uri"]].compact.find do |page|
@@ -22,34 +22,49 @@ module RubyLsp
22
22
  # FOO # should be highlighted as "read"
23
23
  # end
24
24
  # ```
25
- class DocumentHighlight < BaseRequest
25
+ class DocumentHighlight < Listener
26
26
  extend T::Sig
27
27
 
28
- sig { params(document: Document, position: Document::PositionShape).void }
29
- def initialize(document, position)
30
- super(document)
28
+ ResponseType = type_member { { fixed: T::Array[Interface::DocumentHighlight] } }
31
29
 
32
- @highlights = T.let([], T::Array[Interface::DocumentHighlight])
33
- return unless document.parsed?
30
+ sig { override.returns(ResponseType) }
31
+ attr_reader :response
34
32
 
35
- @target = T.let(find(position), T.nilable(Support::HighlightTarget))
33
+ sig do
34
+ params(
35
+ target: T.nilable(SyntaxTree::Node),
36
+ parent: T.nilable(SyntaxTree::Node),
37
+ emitter: EventEmitter,
38
+ message_queue: Thread::Queue,
39
+ ).void
36
40
  end
41
+ def initialize(target, parent, emitter, message_queue)
42
+ super(emitter, message_queue)
43
+
44
+ @response = T.let([], T::Array[Interface::DocumentHighlight])
45
+
46
+ return unless target && parent
37
47
 
38
- sig { override.returns(T.all(T::Array[Interface::DocumentHighlight], Object)) }
39
- def run
40
- # no @target means the target is not highlightable
41
- visit(@document.tree) if @document.parsed? && @target
42
- @highlights
48
+ highlight_target =
49
+ case target
50
+ when *DIRECT_HIGHLIGHTS
51
+ Support::HighlightTarget.new(target)
52
+ when SyntaxTree::Ident
53
+ relevant_node = parent.is_a?(SyntaxTree::Params) ? target : parent
54
+ Support::HighlightTarget.new(relevant_node)
55
+ end
56
+
57
+ @target = T.let(highlight_target, T.nilable(Support::HighlightTarget))
58
+
59
+ emitter.register(self, :on_node) if @target
43
60
  end
44
61
 
45
- sig { override.params(node: T.nilable(SyntaxTree::Node)).void }
46
- def visit(node)
62
+ sig { params(node: T.nilable(SyntaxTree::Node)).void }
63
+ def on_node(node)
47
64
  return if node.nil?
48
65
 
49
66
  match = T.must(@target).highlight_type(node)
50
67
  add_highlight(match) if match
51
-
52
- super
53
68
  end
54
69
 
55
70
  private
@@ -65,30 +80,10 @@ module RubyLsp
65
80
  T::Array[T.class_of(SyntaxTree::Node)],
66
81
  )
67
82
 
68
- sig do
69
- params(
70
- position: Document::PositionShape,
71
- ).returns(T.nilable(Support::HighlightTarget))
72
- end
73
- def find(position)
74
- matched, parent = @document.locate_node(position)
75
-
76
- return unless matched && parent
77
- return unless matched.is_a?(SyntaxTree::Ident) || DIRECT_HIGHLIGHTS.include?(matched.class)
78
-
79
- case matched
80
- when *DIRECT_HIGHLIGHTS
81
- Support::HighlightTarget.new(matched)
82
- when SyntaxTree::Ident
83
- relevant_node = parent.is_a?(SyntaxTree::Params) ? matched : parent
84
- Support::HighlightTarget.new(relevant_node)
85
- end
86
- end
87
-
88
83
  sig { params(match: Support::HighlightTarget::HighlightMatch).void }
89
84
  def add_highlight(match)
90
85
  range = range_from_syntax_tree_node(match.node)
91
- @highlights << Interface::DocumentHighlight.new(range: range, kind: match.type)
86
+ @response << Interface::DocumentHighlight.new(range: range, kind: match.type)
92
87
  end
93
88
  end
94
89
  end
@@ -30,13 +30,11 @@ module RubyLsp
30
30
  def initialize(document, position, trigger_character)
31
31
  super(document)
32
32
 
33
- scanner = document.create_scanner
34
- line_begin = position[:line] == 0 ? 0 : scanner.find_char_position({ line: position[:line] - 1, character: 0 })
35
- @line_end = T.let(scanner.find_char_position(position), Integer)
36
- line = T.must(@document.source[line_begin..@line_end])
33
+ @lines = T.let(@document.source.lines, T::Array[String])
34
+ line = @lines[[position[:line] - 1, 0].max]
37
35
 
38
- @indentation = T.let(find_indentation(line), Integer)
39
- @previous_line = T.let(line.strip.chomp, String)
36
+ @indentation = T.let(line ? find_indentation(line) : 0, Integer)
37
+ @previous_line = T.let(line ? line.strip.chomp : "", String)
40
38
  @position = position
41
39
  @edits = T.let([], T::Array[Interface::TextEdit])
42
40
  @trigger_character = trigger_character
@@ -87,30 +85,15 @@ module RubyLsp
87
85
  return unless END_REGEXES.any? { |regex| regex.match?(@previous_line) }
88
86
 
89
87
  indents = " " * @indentation
88
+ current_line = @lines[@position[:line]]
89
+ next_line = @lines[@position[:line] + 1]
90
90
 
91
- if @previous_line.include?("\n")
92
- # If the previous line has a line break, then it means there's content after the line break that triggered
93
- # this completion. For these cases, we want to add the `end` after the content and move the cursor back to the
94
- # keyword that triggered the completion
95
-
96
- line = @position[:line]
97
-
98
- # If there are enough lines in the document, we want to add the `end` token on the line below the extra
99
- # content. Otherwise, we want to insert and extra line break ourselves
100
- correction = if T.must(@document.source[@line_end..-1]).count("\n") >= 2
101
- line -= 1
102
- "#{indents}end"
103
- else
104
- "#{indents}\nend"
105
- end
106
-
107
- add_edit_with_text(correction, { line: @position[:line] + 1, character: @position[:character] })
108
- move_cursor_to(line, @indentation + 3)
109
- else
110
- # If there's nothing after the new line break that triggered the completion, then we want to add the `end` and
111
- # move the cursor to the body of the statement
91
+ if current_line.nil? || current_line.blank?
112
92
  add_edit_with_text(" \n#{indents}end")
113
93
  move_cursor_to(@position[:line], @indentation + 2)
94
+ elsif next_line.nil? || next_line.blank?
95
+ add_edit_with_text("#{indents}end", { line: @position[:line] + 1, character: @position[:character] })
96
+ move_cursor_to(@position[:line], @indentation + 3)
114
97
  end
115
98
  end
116
99
 
@@ -20,15 +20,19 @@ module RubyLsp
20
20
 
21
21
  sig { returns(String) }
22
22
  def detected_test_library
23
- if direct_dependency?(/^minitest/)
23
+ # A Rails app may have a dependency on minitest, but we would instead want to use the Rails test runner provided
24
+ # by ruby-lsp-rails.
25
+ if direct_dependency?(/^rails$/)
26
+ "rails"
27
+ # NOTE: Intentionally ends with $ to avoid mis-matching minitest-reporters, etc. in a Rails app.
28
+ elsif direct_dependency?(/^minitest$/)
24
29
  "minitest"
25
30
  elsif direct_dependency?(/^test-unit/)
26
31
  "test-unit"
27
32
  elsif direct_dependency?(/^rspec/)
28
33
  "rspec"
29
34
  else
30
- warn("WARNING: No test library detected. Assuming minitest.")
31
- "minitest"
35
+ "unknown"
32
36
  end
33
37
  end
34
38
 
@@ -78,7 +78,7 @@ module URI
78
78
  if URI.respond_to?(:register_scheme)
79
79
  URI.register_scheme("SOURCE", self)
80
80
  else
81
- @@schemes = T.let(@@schemes, T::Hash[String, T::Class[T.anything]]) # rubocop:disable Style/ClassVars
81
+ @@schemes = T.let(@@schemes, T::Hash[String, T.untyped]) # rubocop:disable Style/ClassVars
82
82
  @@schemes["SOURCE"] = self
83
83
  end
84
84
  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.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-06-19 00:00:00.000000000 Z
11
+ date: 2023-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol