ruby-lsp 0.14.2 → 0.14.4

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: 293c03761c9b7d497045546e3bf7ca88a08ed5556ad0ba52c205a9798e820ce5
4
- data.tar.gz: 38157f012f3f1dc944186ac1530dc660d8662cb59ca7b12ad3b117655e0c1709
3
+ metadata.gz: 4fc74feb692f2c2e902e213dc61a4b3d58f14bcc4520fc7db60bdae022484e6f
4
+ data.tar.gz: 98f6d5b939bb945119169e2ca3ce060f275b961d421e752774ebea1c25b74c2d
5
5
  SHA512:
6
- metadata.gz: c4996e277088f015004df0f82e2cc47dc2f979f6658f9dfac19fe495c8c4deced3028b021e330ac0681dbfd79048e9033037c7ce93e6cc044c8ae6d91087e0df
7
- data.tar.gz: 67c5c31c97c279f192b0b3d6c729c97a19f7aac4581eca037c0e5355cc0f3d3ed69fb4007a5f32985e046980324575e2e35a92ce81e8078b41ee5b6db711ac88
6
+ metadata.gz: 8c33776eb483463d1052cdd5a82d1f66d5d507325b09848d0c5d78cea310c218b056bef834f6f6f03e17d87088b65c8cd77f7c214735390e8b5d244a059ea9e7
7
+ data.tar.gz: 7e57548a8dede5695774c27137b6ab23c8029f8ff8c0551bef8b1cf8f6dd920e9bdb423d4905c6a7f9ac360433da99cdec31efc885a120f4c1684acbd3da9582
data/LICENSE.txt CHANGED
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  THE SOFTWARE.
22
-
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img alt="Ruby LSP logo" width="200" src="vscode/icon.png" />
3
+ </p>
4
+
1
5
  [![Build Status](https://github.com/Shopify/ruby-lsp/workflows/CI/badge.svg)](https://github.com/Shopify/ruby-lsp/actions/workflows/ci.yml)
2
6
  [![Ruby LSP extension](https://img.shields.io/badge/VS%20Code-Ruby%20LSP-success?logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
3
7
  [![Ruby DX Slack](https://img.shields.io/badge/Slack-Ruby%20DX-success?logo=slack)](https://join.slack.com/t/ruby-dx/shared_invite/zt-2c8zjlir6-uUDJl8oIwcen_FS_aA~b6Q)
@@ -11,12 +15,38 @@ experience to Ruby developers using modern standards for cross-editor features,
11
15
  Want to discuss Ruby developer experience? Consider joining the public
12
16
  [Ruby DX Slack workspace](https://join.slack.com/t/ruby-dx/shared_invite/zt-2c8zjlir6-uUDJl8oIwcen_FS_aA~b6Q).
13
17
 
18
+ ## Features
19
+
20
+ ![Ruby LSP demo](vscode/extras/ruby_lsp_demo.gif)
21
+
22
+ The Ruby LSP features include
23
+
24
+ - Semantic highlighting
25
+ - Symbol search and code outline
26
+ - RuboCop errors and warnings (diagnostics)
27
+ - Format on save (with RuboCop or Syntax Tree)
28
+ - Format on type
29
+ - Debugging support
30
+ - Running and debugging tests through VS Code's UI
31
+ - Go to definition for classes, modules, constants and required files
32
+ - Showing documentaton on hover for classes, modules and constants
33
+ - Completion for classes, modules, constants and require paths
34
+ - Fuzzy search classes, modules and constants anywhere in the project and its dependencies (workspace symbol)
35
+
36
+ Adding method support for definition, completion, hover and workspace symbol is planned, but not yet completed.
37
+
38
+ See complete information about features [here](https://shopify.github.io/ruby-lsp/RubyLsp/Requests.html).
39
+
40
+ If you experience issues, please see the [troubleshooting
41
+ guide](https://github.com/Shopify/ruby-lsp/blob/main/TROUBLESHOOTING.md).
42
+
14
43
  ## Usage
15
44
 
16
45
  ### With VS Code
17
46
 
18
- If using VS Code, all you have to do is install the [Ruby LSP extension](https://github.com/Shopify/vscode-ruby-lsp) to
19
- get the extra features in the editor. Do not install this gem manually.
47
+ If using VS Code, all you have to do is install the [Ruby LSP
48
+ extension](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp) to get the extra features in the
49
+ editor. Do not install the `ruby-lsp` gem manually.
20
50
 
21
51
  ### With other editors
22
52
 
@@ -27,16 +57,8 @@ The gem can be installed by doing
27
57
  gem install ruby-lsp
28
58
  ```
29
59
 
30
- **NOTE**: starting with v0.7.0, it is no longer recommended to add the `ruby-lsp` to the bundle. The gem will generate a
31
- custom bundle in `.ruby-lsp/Gemfile` which is used to identify the versions of dependencies that should be used for the
32
- application (e.g.: the correct RuboCop version).
33
-
34
- For older versions, if you decide to add the gem to the bundle, it is not necessary to require it.
35
- ```ruby
36
- group :development do
37
- gem "ruby-lsp", require: false
38
- end
39
- ```
60
+ and the language server can be launched running `ruby-lsp` (without bundle exec in order to properly hook into your
61
+ project's dependencies).
40
62
 
41
63
  ### Documentation
42
64
 
@@ -54,7 +76,7 @@ default gems, except for
54
76
  - Gems that only appear under the `:development` group
55
77
  - All Ruby files under `test/**/*.rb`
56
78
 
57
- By creating a `.index.yml` file, these configurations can be overridden and tuned.
79
+ By creating a `.index.yml` file, these configurations can be overridden and tuned. Note that indexing dependent behavior, such as definition, hover, completion or workspace symbol will be impacted by the configurations placed here.
58
80
 
59
81
  ```yaml
60
82
  # Exclude files based on a given pattern. Often used to exclude test files or fixtures
@@ -98,16 +120,13 @@ For instructions on how to create addons, see the [addons documentation](ADDONS.
98
120
 
99
121
  ## Contributing
100
122
 
101
- Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/ruby-lsp.
102
- This project is intended to be a safe, welcoming space for collaboration, and contributors
103
- are expected to adhere to the
104
- [Contributor Covenant](CODE_OF_CONDUCT.md)
105
- code of conduct.
123
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/ruby-lsp. This project is intended to
124
+ be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor
125
+ Covenant](CODE_OF_CONDUCT.md) code of conduct.
106
126
 
107
127
  If you wish to contribute, see [CONTRIBUTING](CONTRIBUTING.md) for development instructions and check out our pinned
108
128
  [roadmap issue](https://github.com/Shopify/ruby-lsp/issues) for a list of tasks to get started.
109
129
 
110
130
  ## License
111
131
 
112
- The gem is available as open source under the terms of the
113
- [MIT License](LICENSE.txt).
132
+ The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.2
1
+ 0.14.4
data/exe/ruby-lsp CHANGED
@@ -94,13 +94,6 @@ if options[:debug]
94
94
  exit 1
95
95
  end
96
96
 
97
- sockets_dir = "/tmp/ruby-lsp-debug-sockets"
98
- Dir.mkdir(sockets_dir) unless Dir.exist?(sockets_dir)
99
- # ruby-debug-ENV["USER"] is an implicit naming pattern in ruby/debug
100
- # if it's not present, rdbg will not find the socket
101
- socket_identifier = "ruby-debug-#{ENV["USER"]}-#{File.basename(Dir.pwd)}.sock"
102
- ENV["RUBY_DEBUG_SOCK_PATH"] = "#{sockets_dir}/#{socket_identifier}"
103
-
104
97
  begin
105
98
  require "debug/open_nonstop"
106
99
  rescue LoadError
@@ -121,24 +121,10 @@ module RubyLsp
121
121
  begin
122
122
  formatting(uri)
123
123
  rescue Requests::Formatting::InvalidFormatter => error
124
- @message_queue << Notification.new(
125
- message: "window/showMessage",
126
- params: Interface::ShowMessageParams.new(
127
- type: Constant::MessageType::ERROR,
128
- message: "Configuration error: #{error.message}",
129
- ),
130
- )
131
-
124
+ @message_queue << Notification.window_show_error("Configuration error: #{error.message}")
132
125
  nil
133
126
  rescue StandardError, LoadError => error
134
- @message_queue << Notification.new(
135
- message: "window/showMessage",
136
- params: Interface::ShowMessageParams.new(
137
- type: Constant::MessageType::ERROR,
138
- message: "Formatting error: #{error.message}",
139
- ),
140
- )
141
-
127
+ @message_queue << Notification.window_show_error("Formatting error: #{error.message}")
142
128
  nil
143
129
  end
144
130
  when "textDocument/documentHighlight"
@@ -174,14 +160,7 @@ module RubyLsp
174
160
  begin
175
161
  diagnostic(uri)
176
162
  rescue StandardError, LoadError => error
177
- @message_queue << Notification.new(
178
- message: "window/showMessage",
179
- params: Interface::ShowMessageParams.new(
180
- type: Constant::MessageType::ERROR,
181
- message: "Error running diagnostics: #{error.message}",
182
- ),
183
- )
184
-
163
+ @message_queue << Notification.window_show_error("Error running diagnostics: #{error.message}")
185
164
  nil
186
165
  end
187
166
  when "textDocument/completion"
@@ -287,13 +266,7 @@ module RubyLsp
287
266
  false
288
267
  end
289
268
  rescue StandardError => error
290
- @message_queue << Notification.new(
291
- message: "window/showMessage",
292
- params: Interface::ShowMessageParams.new(
293
- type: Constant::MessageType::ERROR,
294
- message: "Error while indexing: #{error.message}",
295
- ),
296
- )
269
+ @message_queue << Notification.window_show_error("Error while indexing: #{error.message}")
297
270
  end
298
271
 
299
272
  # Always end the progress notification even if indexing failed or else it never goes away and the user has no
@@ -402,21 +375,11 @@ module RubyLsp
402
375
 
403
376
  case result
404
377
  when Requests::CodeActionResolve::Error::EmptySelection
405
- @message_queue << Notification.new(
406
- message: "window/showMessage",
407
- params: Interface::ShowMessageParams.new(
408
- type: Constant::MessageType::ERROR,
409
- message: "Invalid selection for Extract Variable refactor",
410
- ),
411
- )
378
+ @message_queue << Notification.window_show_error("Invalid selection for Extract Variable refactor")
412
379
  raise Requests::CodeActionResolve::CodeActionError
413
380
  when Requests::CodeActionResolve::Error::InvalidTargetRange
414
- @message_queue << Notification.new(
415
- message: "window/showMessage",
416
- params: Interface::ShowMessageParams.new(
417
- type: Constant::MessageType::ERROR,
418
- message: "Couldn't find an appropriate location to place extracted refactor",
419
- ),
381
+ @message_queue << Notification.window_show_error(
382
+ "Couldn't find an appropriate location to place extracted refactor",
420
383
  )
421
384
  raise Requests::CodeActionResolve::CodeActionError
422
385
  else
@@ -639,12 +602,8 @@ module RubyLsp
639
602
  unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
640
603
  @store.formatter = "none"
641
604
 
642
- @message_queue << Notification.new(
643
- message: "window/showMessage",
644
- params: Interface::ShowMessageParams.new(
645
- type: Constant::MessageType::ERROR,
646
- message: "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the Gemfile or gemspec.",
647
- ),
605
+ @message_queue << Notification.window_show_error(
606
+ "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the Gemfile or gemspec.",
648
607
  )
649
608
  end
650
609
  end
@@ -14,30 +14,24 @@ module RubyLsp
14
14
  nesting: T::Array[String],
15
15
  typechecker_enabled: T::Boolean,
16
16
  dispatcher: Prism::Dispatcher,
17
+ uri: URI::Generic,
17
18
  ).void
18
19
  end
19
- def initialize(response_builder, index, nesting, typechecker_enabled, dispatcher)
20
+ def initialize(response_builder, index, nesting, typechecker_enabled, dispatcher, uri) # rubocop:disable Metrics/ParameterLists
20
21
  @response_builder = response_builder
21
22
  @index = index
22
23
  @nesting = nesting
23
24
  @typechecker_enabled = typechecker_enabled
25
+ @uri = uri
24
26
 
25
27
  dispatcher.register(
26
28
  self,
27
- :on_string_node_enter,
28
29
  :on_constant_path_node_enter,
29
30
  :on_constant_read_node_enter,
30
31
  :on_call_node_enter,
31
32
  )
32
33
  end
33
34
 
34
- sig { params(node: Prism::StringNode).void }
35
- def on_string_node_enter(node)
36
- @index.search_require_paths(node.content).map!(&:require_path).sort!.each do |path|
37
- @response_builder << build_completion(T.must(path), node)
38
- end
39
- end
40
-
41
35
  # Handle completion on regular constant references (e.g. `Bar`)
42
36
  sig { params(node: Prism::ConstantReadNode).void }
43
37
  def on_constant_read_node_enter(node)
@@ -107,11 +101,64 @@ module RubyLsp
107
101
  sig { params(node: Prism::CallNode).void }
108
102
  def on_call_node_enter(node)
109
103
  return if @typechecker_enabled
110
- return unless self_receiver?(node)
111
104
 
112
105
  name = node.message
113
106
  return unless name
114
107
 
108
+ case name
109
+ when "require"
110
+ complete_require(node)
111
+ when "require_relative"
112
+ complete_require_relative(node)
113
+ else
114
+ complete_self_receiver_method(node, name) if self_receiver?(node)
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ sig { params(node: Prism::CallNode).void }
121
+ def complete_require(node)
122
+ arguments_node = node.arguments
123
+ return unless arguments_node
124
+
125
+ path_node_to_complete = arguments_node.arguments.first
126
+
127
+ return unless path_node_to_complete.is_a?(Prism::StringNode)
128
+
129
+ @index.search_require_paths(path_node_to_complete.content).map!(&:require_path).sort!.each do |path|
130
+ @response_builder << build_completion(T.must(path), path_node_to_complete)
131
+ end
132
+ end
133
+
134
+ sig { params(node: Prism::CallNode).void }
135
+ def complete_require_relative(node)
136
+ arguments_node = node.arguments
137
+ return unless arguments_node
138
+
139
+ path_node_to_complete = arguments_node.arguments.first
140
+
141
+ return unless path_node_to_complete.is_a?(Prism::StringNode)
142
+
143
+ origin_dir = Pathname.new(@uri.to_standardized_path).dirname
144
+
145
+ path_query = path_node_to_complete.content
146
+ # if the path is not a directory, glob all possible next characters
147
+ # for example ../somethi| (where | is the cursor position)
148
+ # should find files for ../somethi*/
149
+ path_query += "*/" unless path_query.end_with?("/")
150
+ path_query += "**/*.rb"
151
+
152
+ Dir.glob(path_query, base: origin_dir).sort!.each do |path|
153
+ @response_builder << build_completion(
154
+ path.delete_suffix(".rb"),
155
+ path_node_to_complete,
156
+ )
157
+ end
158
+ end
159
+
160
+ sig { params(node: Prism::CallNode, name: String).void }
161
+ def complete_self_receiver_method(node, name)
115
162
  receiver_entries = @index[@nesting.join("::")]
116
163
  return unless receiver_entries
117
164
 
@@ -125,8 +172,6 @@ module RubyLsp
125
172
  end
126
173
  end
127
174
 
128
- private
129
-
130
175
  sig do
131
176
  params(
132
177
  entry: RubyIndexer::Entry::Member,
@@ -139,7 +184,7 @@ module RubyLsp
139
184
  Interface::CompletionItem.new(
140
185
  label: name,
141
186
  filter_text: name,
142
- text_edit: Interface::TextEdit.new(range: range_from_node(node), new_text: name),
187
+ text_edit: Interface::TextEdit.new(range: range_from_location(T.must(node.message_loc)), new_text: name),
143
188
  kind: Constant::CompletionItemKind::METHOD,
144
189
  label_details: Interface::CompletionItemLabelDetails.new(
145
190
  detail: "(#{entry.parameters.map(&:decorated_name).join(", ")})",
@@ -68,26 +68,13 @@ module RubyLsp
68
68
  ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
69
69
  )
70
70
 
71
- Listeners::Completion.new(@response_builder, index, nesting, typechecker_enabled, dispatcher)
71
+ Listeners::Completion.new(@response_builder, index, nesting, typechecker_enabled, dispatcher, document.uri)
72
72
 
73
73
  return unless matched && parent
74
74
 
75
75
  @target = case matched
76
76
  when Prism::CallNode
77
- message = matched.message
78
-
79
- if message == "require"
80
- args = matched.arguments&.arguments
81
- return if args.nil? || args.is_a?(Prism::ForwardingArgumentsNode)
82
-
83
- argument = args.first
84
- return unless argument.is_a?(Prism::StringNode)
85
- return unless (argument.location.start_offset..argument.location.end_offset).cover?(char_position)
86
-
87
- argument
88
- else
89
- matched
90
- end
77
+ matched
91
78
  when Prism::ConstantReadNode, Prism::ConstantPathNode
92
79
  if parent.is_a?(Prism::ConstantPathNode) && matched.is_a?(Prism::ConstantReadNode)
93
80
  parent
@@ -42,27 +42,61 @@ module RubyLsp
42
42
 
43
43
  sig { override.returns(T.nilable(T.all(T::Array[Interface::Diagnostic], Object))) }
44
44
  def perform
45
+ diagnostics = []
46
+ diagnostics.concat(syntax_error_diagnostics, syntax_warning_diagnostics)
47
+
45
48
  # Running RuboCop is slow, so to avoid excessive runs we only do so if the file is syntactically valid
46
- return syntax_error_diagnostics if @document.syntax_error?
47
- return [] unless defined?(Support::RuboCopDiagnosticsRunner)
49
+ return diagnostics if @document.syntax_error?
50
+
51
+ diagnostics.concat(
52
+ Support::RuboCopDiagnosticsRunner.instance.run(
53
+ @uri,
54
+ @document,
55
+ ).map!(&:to_lsp_diagnostic),
56
+ ) if defined?(Support::RuboCopDiagnosticsRunner)
48
57
 
49
- Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document).map!(&:to_lsp_diagnostic)
58
+ diagnostics
50
59
  end
51
60
 
52
61
  private
53
62
 
54
- sig { returns(T.nilable(T::Array[Interface::Diagnostic])) }
63
+ sig { returns(T::Array[Interface::Diagnostic]) }
64
+ def syntax_warning_diagnostics
65
+ @document.parse_result.warnings.map do |warning|
66
+ location = warning.location
67
+
68
+ Interface::Diagnostic.new(
69
+ source: "Prism",
70
+ message: warning.message,
71
+ severity: Constant::DiagnosticSeverity::WARNING,
72
+ range: Interface::Range.new(
73
+ start: Interface::Position.new(
74
+ line: location.start_line - 1,
75
+ character: location.start_column,
76
+ ),
77
+ end: Interface::Position.new(
78
+ line: location.end_line - 1,
79
+ character: location.end_column,
80
+ ),
81
+ ),
82
+ )
83
+ end
84
+ end
85
+
86
+ sig { returns(T::Array[Interface::Diagnostic]) }
55
87
  def syntax_error_diagnostics
56
88
  @document.parse_result.errors.map do |error|
89
+ location = error.location
90
+
57
91
  Interface::Diagnostic.new(
58
92
  range: Interface::Range.new(
59
93
  start: Interface::Position.new(
60
- line: error.location.start_line - 1,
61
- character: error.location.start_column,
94
+ line: location.start_line - 1,
95
+ character: location.start_column,
62
96
  ),
63
97
  end: Interface::Position.new(
64
- line: error.location.end_line - 1,
65
- character: error.location.end_column,
98
+ line: location.end_line - 1,
99
+ character: location.end_column,
66
100
  ),
67
101
  ),
68
102
  message: error.message,
@@ -178,7 +178,7 @@ module RubyLsp
178
178
 
179
179
  sig { params(line: Integer, character: Integer).void }
180
180
  def move_cursor_to(line, character)
181
- return if @client_name != "Visual Studio Code"
181
+ return unless @client_name.start_with?("Visual Studio Code")
182
182
 
183
183
  position = Interface::Position.new(
184
184
  line: line,
@@ -41,7 +41,22 @@ module RubyLsp
41
41
  end
42
42
  end
43
43
 
44
- class Notification < Message; end
44
+ class Notification < Message
45
+ class << self
46
+ extend T::Sig
47
+ sig { params(message: String).returns(Notification) }
48
+ def window_show_error(message)
49
+ new(
50
+ message: "window/showMessage",
51
+ params: Interface::ShowMessageParams.new(
52
+ type: Constant::MessageType::ERROR,
53
+ message: message,
54
+ ),
55
+ )
56
+ end
57
+ end
58
+ end
59
+
45
60
  class Request < Message; end
46
61
 
47
62
  # The final result of running a request before its IO is finalized
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.14.2
4
+ version: 0.14.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-22 00:00:00.000000000 Z
11
+ date: 2024-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol