ruby-lsp 0.3.0 → 0.3.1

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