ruby-lsp 0.6.1 → 0.6.2

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