ruby-lsp 0.3.0 → 0.3.1

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: 6e66f508823e7d8e663c90e9990ae13bf26c3480d4ce9d7d43db14185518064d
4
- data.tar.gz: 70cfc0be15d1ad988d66a641191fa64679109ae1f6be56afdc01ded665b0a439
3
+ metadata.gz: 9762389a7b7979afb57d6e6d2cf0d65b0b0a0b31f5a1fdd7045311c33a0c21d6
4
+ data.tar.gz: 35cc2318946e650ee311129955d3674c7e693a87d25c5cc2f29c5a55e479d880
5
5
  SHA512:
6
- metadata.gz: a2f71a448863bf25e229032cb06d3ae7e7ebf1ef50b414e6486bd902ec7f100dad1ae4cb7160ef9c5fa8069f3ff0bcbb3da29d9c16a49f2e347f8d3d8797049a
7
- data.tar.gz: 4cd747fb8505531a1ab0f1c28c8b3a09a134d544c1834b2f821c220bce2e3b6efce325b73326e23d92891a27ae5ae3cd9a79e9bbfdfa1b45c8f6e490b74d5c9a
6
+ metadata.gz: 4735e18c297c992727d0a151ed7948cf6d4ea32b01fe411ed0774efdda7cdc0cf6533f5d71ad288aad9e6e717149932744376fdceda9fa4cd039b560934a9b47
7
+ data.tar.gz: bd6e0c9a81a373c509f2f96b480543657a8d241a6e58ea25ec21c62ac60094f299af1c5f85d1555ad6ca1908c325e46569119271d4c6b7117f33f9919cf40bca
data/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.1]
10
+
11
+ - Resolve TODO for LSP v3.17 (https://github.com/Shopify/ruby-lsp/pull/268)
12
+ - Add dependency constraint for LSP v3.17 (https://github.com/Shopify/ruby-lsp/pull/269)
13
+ - Handle class/module declarations as a class token with declaration modifier (https://github.com/Shopify/ruby-lsp/pull/260)
14
+ - Handle required parameters in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/271)
15
+ - Add comment continuation via on type on_type_formatting (https://github.com/Shopify/ruby-lsp/pull/274)
16
+ - Make RuboCop runner use composition instead of inheritance (https://github.com/Shopify/ruby-lsp/pull/278)
17
+ - Protect worker against cancellation during popping (https://github.com/Shopify/ruby-lsp/pull/280)
18
+ - Handle formatting errors in on_error block (https://github.com/Shopify/ruby-lsp/pull/279)
19
+ - Fix on type formatting pipe completion for regular or expressions (https://github.com/Shopify/ruby-lsp/pull/282)
20
+ - Do not fail on LoadError (https://github.com/Shopify/ruby-lsp/pull/292)
21
+
9
22
  ## [0.3.0]
10
23
  - Add on type formatting completions (https://github.com/Shopify/ruby-lsp/pull/253)
11
24
  - Upgrade syntax_tree requirement to >= 3.4 (https://github.com/Shopify/ruby-lsp/pull/254)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
@@ -20,13 +20,13 @@ module RubyLsp
20
20
  const :action, T.proc.params(request: T::Hash[Symbol, T.untyped]).returns(T.untyped)
21
21
  const :parallel, T::Boolean
22
22
  prop :error_handler,
23
- T.nilable(T.proc.params(error: StandardError, request: T::Hash[Symbol, T.untyped]).void)
23
+ T.nilable(T.proc.params(error: Exception, request: T::Hash[Symbol, T.untyped]).void)
24
24
 
25
25
  # A proc that runs in case a request has errored. Receives the error and the original request as arguments. Useful
26
26
  # for displaying window messages on errors
27
27
  sig do
28
28
  params(
29
- block: T.proc.bind(Handler).params(error: StandardError, request: T::Hash[Symbol, T.untyped]).void
29
+ block: T.proc.bind(Handler).params(error: Exception, request: T::Hash[Symbol, T.untyped]).void
30
30
  ).void
31
31
  end
32
32
  def on_error(&block)
@@ -7,11 +7,11 @@ module RubyLsp
7
7
  class Queue
8
8
  extend T::Sig
9
9
 
10
- class Cancelled < Interrupt; end
10
+ class Cancelled < StandardError; end
11
11
 
12
12
  class Result < T::Struct
13
13
  const :response, T.untyped # rubocop:disable Sorbet/ForbidUntypedStructProps
14
- const :error, T.nilable(StandardError)
14
+ const :error, T.nilable(Exception)
15
15
  const :request_time, T.nilable(Float)
16
16
  end
17
17
 
@@ -88,11 +88,13 @@ module RubyLsp
88
88
  sig { params(request: T::Hash[Symbol, T.untyped]).returns(Queue::Result) }
89
89
  def execute(request)
90
90
  response = T.let(nil, T.untyped)
91
- error = T.let(nil, T.nilable(StandardError))
91
+ error = T.let(nil, T.nilable(Exception))
92
92
 
93
93
  request_time = Benchmark.realtime do
94
94
  response = T.must(@handlers[request[:method]]).action.call(request)
95
- rescue StandardError => e
95
+ rescue Cancelled
96
+ raise
97
+ rescue StandardError, LoadError => e
96
98
  error = e
97
99
  end
98
100
 
@@ -135,7 +137,10 @@ module RubyLsp
135
137
  def new_worker
136
138
  Thread.new do
137
139
  # Thread::Queue#pop is thread safe and will wait until an item is available
138
- while (job = T.let(@job_queue.pop, T.nilable(Job)))
140
+ loop do
141
+ job = T.let(@job_queue.pop, T.nilable(Job))
142
+ break if job.nil?
143
+
139
144
  # The only time when the job is nil is when the queue is closed and we can then terminate the thread
140
145
 
141
146
  request = job.request
@@ -144,17 +149,18 @@ module RubyLsp
144
149
  @current_job = job
145
150
  end
146
151
 
147
- begin
148
- next if job.cancelled
152
+ next if job.cancelled
149
153
 
150
- result = execute(request)
151
- rescue Cancelled
152
- # We need to return nil to the client even if the request was cancelled
153
- result = Queue::Result.new(response: nil, error: nil, request_time: nil)
154
- ensure
155
- @mutex.synchronize { @current_job = nil }
156
- finalize_request(result, request) unless result.nil?
157
- end
154
+ result = execute(request)
155
+ rescue Cancelled
156
+ # We need to return nil to the client even if the request was cancelled
157
+ result = Queue::Result.new(response: nil, error: nil, request_time: nil)
158
+ ensure
159
+ @mutex.synchronize { @current_job = nil }
160
+
161
+ # If there's request, it means the worker was cancelled while waiting to pop from the queue or immediately
162
+ # after
163
+ finalize_request(result, request) unless result.nil? || request.nil?
158
164
  end
159
165
  end
160
166
  end
@@ -163,7 +169,7 @@ module RubyLsp
163
169
  params(
164
170
  request: T::Hash[Symbol, T.untyped],
165
171
  request_time: Float,
166
- error: T.nilable(StandardError)
172
+ error: T.nilable(Exception)
167
173
  ).returns(T::Hash[Symbol, T.any(String, Float)])
168
174
  end
169
175
  def telemetry_params(request, request_time, error)
@@ -19,6 +19,8 @@ module RubyLsp
19
19
  # end
20
20
  # ```
21
21
  class Formatting < BaseRequest
22
+ class Error < StandardError; end
23
+
22
24
  extend T::Sig
23
25
 
24
26
  sig { params(uri: String, document: Document).void }
@@ -41,7 +41,9 @@ module RubyLsp
41
41
 
42
42
  sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
43
43
  def run
44
- return unless @document.syntax_errors?
44
+ handle_comment_line
45
+
46
+ return @edits unless @document.syntax_errors?
45
47
 
46
48
  case @trigger_character
47
49
  when "{"
@@ -59,7 +61,7 @@ module RubyLsp
59
61
 
60
62
  sig { void }
61
63
  def handle_pipe
62
- return unless /".*|/.match?(@previous_line)
64
+ return unless /((?<=do)|(?<={))\s+\|/.match?(@previous_line)
63
65
 
64
66
  add_edit_with_text("|")
65
67
  move_cursor_to(@position[:line], @position[:character])
@@ -83,6 +85,18 @@ module RubyLsp
83
85
  move_cursor_to(@position[:line], @indentation + 2)
84
86
  end
85
87
 
88
+ sig { void }
89
+ def handle_comment_line
90
+ return unless @trigger_character == "\n"
91
+
92
+ is_comment_match = @previous_line.match(/^#(\s*)/)
93
+ return unless is_comment_match
94
+
95
+ spaces = T.must(is_comment_match[1])
96
+ add_edit_with_text("##{spaces}")
97
+ move_cursor_to(@position[:line], @indentation + spaces.size + 1)
98
+ end
99
+
86
100
  sig { params(text: String).void }
87
101
  def add_edit_with_text(text)
88
102
  position = Interface::Position.new(
@@ -185,6 +185,10 @@ module RubyLsp
185
185
  add_token(location_without_colon(location), :variable)
186
186
  end
187
187
 
188
+ node.requireds.each do |required|
189
+ add_token(required.location, :variable)
190
+ end
191
+
188
192
  rest = node.keyword_rest
189
193
  return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
190
194
 
@@ -217,6 +221,19 @@ module RubyLsp
217
221
  add_token(node.value.location, :method) unless special_method?(node.value.value)
218
222
  end
219
223
 
224
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
225
+ def visit_class(node)
226
+ add_token(node.constant.location, :class, [:declaration])
227
+ add_token(node.superclass.location, :class) if node.superclass
228
+ visit(node.bodystmt)
229
+ end
230
+
231
+ sig { params(node: SyntaxTree::ModuleDeclaration).void }
232
+ def visit_module(node)
233
+ add_token(node.constant.location, :class, [:declaration])
234
+ visit(node.bodystmt)
235
+ end
236
+
220
237
  sig { params(location: SyntaxTree::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
221
238
  def add_token(location, type, modifiers = [])
222
239
  length = location.end_char - location.start_char
@@ -1,11 +1,8 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- begin
5
- require "rubocop"
6
- rescue LoadError
7
- return
8
- end
4
+ require "ruby_lsp/requests/support/rubocop_runner"
5
+ return unless defined?(::RubyLsp::Requests::Support::RuboCopRunner)
9
6
 
10
7
  require "cgi"
11
8
  require "singleton"
@@ -14,46 +11,24 @@ module RubyLsp
14
11
  module Requests
15
12
  module Support
16
13
  # :nodoc:
17
- class RuboCopDiagnosticsRunner < RuboCop::Runner
14
+ class RuboCopDiagnosticsRunner
18
15
  extend T::Sig
19
16
  include Singleton
20
17
 
21
18
  sig { void }
22
19
  def initialize
23
- @options = T.let({}, T::Hash[Symbol, T.untyped])
24
- @uri = T.let(nil, T.nilable(String))
25
- @diagnostics = T.let([], T::Array[Support::RuboCopDiagnostic])
26
-
27
- super(
28
- ::RuboCop::Options.new.parse([
29
- "--stderr", # Print any output to stderr so that our stdout does not get polluted
30
- "--force-exclusion",
31
- "--format",
32
- "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
33
- ]).first,
34
- ::RuboCop::ConfigStore.new
35
- )
20
+ @runner = T.let(RuboCopRunner.new, RuboCopRunner)
36
21
  end
37
22
 
38
23
  sig { params(uri: String, document: Document).returns(T::Array[Support::RuboCopDiagnostic]) }
39
24
  def run(uri, document)
40
- @diagnostics.clear
41
- @uri = uri
42
-
43
- file = CGI.unescape(URI.parse(uri).path)
44
- # We communicate with Rubocop via stdin
45
- @options[:stdin] = document.source
46
-
25
+ filename = CGI.unescape(URI.parse(uri).path)
47
26
  # Invoke RuboCop with just this file in `paths`
48
- super([file])
49
- @diagnostics
50
- end
51
-
52
- private
27
+ @runner.run(filename, document.source)
53
28
 
54
- sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
55
- def file_finished(_file, offenses)
56
- @diagnostics = offenses.map { |offense| Support::RuboCopDiagnostic.new(offense, T.must(@uri)) }
29
+ @runner.offenses.map do |offense|
30
+ Support::RuboCopDiagnostic.new(offense, uri)
31
+ end
57
32
  end
58
33
  end
59
34
  end
@@ -1,11 +1,8 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- begin
5
- require "rubocop"
6
- rescue LoadError
7
- return
8
- end
4
+ require "ruby_lsp/requests/support/rubocop_runner"
5
+ return unless defined?(::RubyLsp::Requests::Support::RuboCopRunner)
9
6
 
10
7
  require "cgi"
11
8
  require "singleton"
@@ -14,35 +11,24 @@ module RubyLsp
14
11
  module Requests
15
12
  module Support
16
13
  # :nodoc:
17
- class RuboCopFormattingRunner < RuboCop::Runner
14
+ class RuboCopFormattingRunner
18
15
  extend T::Sig
19
16
  include Singleton
20
17
 
21
18
  sig { void }
22
19
  def initialize
23
- @options = T.let({}, T::Hash[Symbol, T.untyped])
24
-
25
- super(
26
- ::RuboCop::Options.new.parse([
27
- "--stderr", # Print any output to stderr so that our stdout does not get polluted
28
- "--force-exclusion",
29
- "--format",
30
- "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
31
- "-a", # --auto-correct
32
- ]).first,
33
- ::RuboCop::ConfigStore.new
34
- )
20
+ # -a is for "--auto-correct" (or "--autocorrect" on newer versions of RuboCop)
21
+ @runner = T.let(RuboCopRunner.new("-a"), RuboCopRunner)
35
22
  end
36
23
 
37
24
  sig { params(uri: String, document: Document).returns(T.nilable(String)) }
38
25
  def run(uri, document)
39
- file = CGI.unescape(URI.parse(uri).path)
40
- # We communicate with Rubocop via stdin
41
- @options[:stdin] = document.source
26
+ filename = CGI.unescape(URI.parse(uri).path)
42
27
 
43
28
  # Invoke RuboCop with just this file in `paths`
44
- super([file])
45
- @options[:stdin]
29
+ @runner.run(filename, document.source)
30
+
31
+ @runner.formatted_source
46
32
  end
47
33
  end
48
34
  end
@@ -0,0 +1,69 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "rubocop"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ module RubyLsp
11
+ module Requests
12
+ module Support
13
+ # :nodoc:
14
+ class RuboCopRunner < RuboCop::Runner
15
+ extend T::Sig
16
+
17
+ sig { returns(T::Array[RuboCop::Cop::Offense]) }
18
+ attr_reader :offenses
19
+
20
+ DEFAULT_ARGS = T.let([
21
+ "--stderr", # Print any output to stderr so that our stdout does not get polluted
22
+ "--force-exclusion",
23
+ "--format",
24
+ "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
25
+ ].freeze, T::Array[String])
26
+
27
+ sig { params(args: String).void }
28
+ def initialize(*args)
29
+ @options = T.let({}, T::Hash[Symbol, T.untyped])
30
+ @offenses = T.let([], T::Array[RuboCop::Cop::Offense])
31
+ @errors = T.let([], T::Array[String])
32
+ @warnings = T.let([], T::Array[String])
33
+
34
+ args += DEFAULT_ARGS
35
+ rubocop_options = ::RuboCop::Options.new.parse(args).first
36
+ config_store = ::RuboCop::ConfigStore.new
37
+
38
+ super(rubocop_options, config_store)
39
+ end
40
+
41
+ sig { params(path: String, contents: String).void }
42
+ def run(path, contents)
43
+ # Clear Runner state between runs since we get a single instance of this class
44
+ # on every use site.
45
+ @errors = []
46
+ @warnings = []
47
+ @offenses = []
48
+ @options[:stdin] = contents
49
+
50
+ super([path])
51
+ rescue RuboCop::Runner::InfiniteCorrectionLoop => error
52
+ raise Formatting::Error, error.message
53
+ end
54
+
55
+ sig { returns(String) }
56
+ def formatted_source
57
+ @options[:stdin]
58
+ end
59
+
60
+ private
61
+
62
+ sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
63
+ def file_finished(_file, offenses)
64
+ @offenses = offenses
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -55,24 +55,23 @@ module RubyLsp
55
55
  )
56
56
  end
57
57
 
58
- # TODO: switch back to using Interface::ServerCapabilities once the gem is updated for spec 3.17
59
58
  Interface::InitializeResult.new(
60
- capabilities: {
61
- textDocumentSync: Interface::TextDocumentSyncOptions.new(
59
+ capabilities: Interface::ServerCapabilities.new(
60
+ text_document_sync: Interface::TextDocumentSyncOptions.new(
62
61
  change: Constant::TextDocumentSyncKind::INCREMENTAL,
63
62
  open_close: true,
64
63
  ),
65
- selectionRangeProvider: enabled_features.include?("selectionRanges"),
66
- documentSymbolProvider: document_symbol_provider,
67
- documentLinkProvider: document_link_provider,
68
- foldingRangeProvider: folding_ranges_provider,
69
- semanticTokensProvider: semantic_tokens_provider,
70
- documentFormattingProvider: enabled_features.include?("formatting"),
71
- documentHighlightProvider: enabled_features.include?("documentHighlights"),
72
- codeActionProvider: enabled_features.include?("codeActions"),
73
- documentOnTypeFormattingProvider: on_type_formatting_provider,
74
- diagnosticProvider: diagnostics_provider,
75
- }.reject { |_, v| !v }
64
+ selection_range_provider: enabled_features.include?("selectionRanges"),
65
+ document_symbol_provider: document_symbol_provider,
66
+ document_link_provider: document_link_provider,
67
+ folding_range_provider: folding_ranges_provider,
68
+ semantic_tokens_provider: semantic_tokens_provider,
69
+ document_formatting_provider: enabled_features.include?("formatting"),
70
+ document_highlight_provider: enabled_features.include?("documentHighlights"),
71
+ code_action_provider: enabled_features.include?("codeActions"),
72
+ document_on_type_formatting_provider: on_type_formatting_provider,
73
+ diagnostic_provider: diagnostics_provider,
74
+ )
76
75
  )
77
76
  end
78
77
 
@@ -156,9 +155,8 @@ module RubyLsp
156
155
  uri = request.dig(:params, :textDocument, :uri)
157
156
 
158
157
  Requests::Formatting.new(uri, store.get(uri)).run
159
- rescue RuboCop::Runner::InfiniteCorrectionLoop => e
160
- show_message(Constant::MessageType::ERROR, "Error from RuboCop: #{e.message}")
161
- nil
158
+ end.on_error do |error|
159
+ show_message(Constant::MessageType::ERROR, "Formatting error: #{error.message}")
162
160
  end
163
161
 
164
162
  on("textDocument/onTypeFormatting", parallel: true) do |request|
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-26 00:00:00.000000000 Z
11
+ date: 2022-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 3.17.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 3.17.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sorbet-runtime
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -86,6 +86,7 @@ files:
86
86
  - lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
87
87
  - lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb
88
88
  - lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb
89
+ - lib/ruby_lsp/requests/support/rubocop_runner.rb
89
90
  - lib/ruby_lsp/requests/support/selection_range.rb
90
91
  - lib/ruby_lsp/requests/support/semantic_token_encoder.rb
91
92
  - lib/ruby_lsp/requests/support/source_uri.rb