ruby-lsp 0.0.4 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +11 -1
  3. data/CHANGELOG.md +35 -0
  4. data/Gemfile +4 -3
  5. data/Gemfile.lock +26 -24
  6. data/README.md +3 -2
  7. data/Rakefile +8 -1
  8. data/VERSION +1 -1
  9. data/bin/console +19 -0
  10. data/exe/ruby-lsp +1 -3
  11. data/lib/ruby_lsp/document.rb +17 -4
  12. data/lib/ruby_lsp/handler.rb +54 -141
  13. data/lib/{internal.rb → ruby_lsp/internal.rb} +4 -2
  14. data/lib/ruby_lsp/requests/base_request.rb +9 -7
  15. data/lib/ruby_lsp/requests/code_actions.rb +20 -9
  16. data/lib/ruby_lsp/requests/diagnostics.rb +25 -8
  17. data/lib/ruby_lsp/requests/document_highlight.rb +32 -32
  18. data/lib/ruby_lsp/requests/document_symbol.rb +59 -10
  19. data/lib/ruby_lsp/requests/folding_ranges.rb +73 -34
  20. data/lib/ruby_lsp/requests/formatting.rb +25 -15
  21. data/lib/ruby_lsp/requests/selection_ranges.rb +18 -5
  22. data/lib/ruby_lsp/requests/semantic_highlighting.rb +179 -36
  23. data/lib/ruby_lsp/requests/support/highlight_target.rb +87 -0
  24. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +16 -4
  25. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +61 -0
  26. data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +50 -0
  27. data/lib/ruby_lsp/requests/support/selection_range.rb +4 -1
  28. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +13 -3
  29. data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +6 -1
  30. data/lib/ruby_lsp/requests.rb +13 -2
  31. data/lib/ruby_lsp/server.rb +160 -0
  32. data/lib/ruby_lsp/store.rb +17 -9
  33. data/rakelib/check_docs.rake +30 -5
  34. data/ruby-lsp.gemspec +6 -5
  35. data/sorbet/tapioca/require.rb +1 -1
  36. metadata +14 -26
  37. data/lib/ruby_lsp/cli.rb +0 -88
  38. data/lib/ruby_lsp/requests/rubocop_request.rb +0 -50
  39. data/shipit.production.yml +0 -1
@@ -1,8 +1,10 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
+ # ![Selection ranges demo](../../misc/selection_ranges.gif)
7
+ #
6
8
  # The [selection ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange)
7
9
  # request informs the editor of ranges that the user may want to select based on the location(s)
8
10
  # of their cursor(s).
@@ -17,7 +19,9 @@ module RubyLsp
17
19
  # end
18
20
  # ```
19
21
  class SelectionRanges < BaseRequest
20
- NODES_THAT_CAN_BE_PARENTS = [
22
+ extend T::Sig
23
+
24
+ NODES_THAT_CAN_BE_PARENTS = T.let([
21
25
  SyntaxTree::Assign,
22
26
  SyntaxTree::ArrayLiteral,
23
27
  SyntaxTree::Begin,
@@ -54,15 +58,17 @@ module RubyLsp
54
58
  SyntaxTree::VCall,
55
59
  SyntaxTree::When,
56
60
  SyntaxTree::While,
57
- ].freeze
61
+ ].freeze, T::Array[T.class_of(SyntaxTree::Node)])
58
62
 
63
+ sig { params(document: Document).void }
59
64
  def initialize(document)
60
65
  super(document)
61
66
 
62
- @ranges = []
63
- @stack = []
67
+ @ranges = T.let([], T::Array[Support::SelectionRange])
68
+ @stack = T.let([], T::Array[Support::SelectionRange])
64
69
  end
65
70
 
71
+ sig { override.returns(T.all(T::Array[Support::SelectionRange], Object)) }
66
72
  def run
67
73
  visit(@document.tree)
68
74
  @ranges.reverse!
@@ -70,6 +76,7 @@ module RubyLsp
70
76
 
71
77
  private
72
78
 
79
+ sig { params(node: T.nilable(SyntaxTree::Node)).void }
73
80
  def visit(node)
74
81
  return if node.nil?
75
82
 
@@ -83,6 +90,12 @@ module RubyLsp
83
90
  @stack.pop if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
84
91
  end
85
92
 
93
+ sig do
94
+ params(
95
+ location: SyntaxTree::Location,
96
+ parent: T.nilable(Support::SelectionRange)
97
+ ).returns(Support::SelectionRange)
98
+ end
86
99
  def create_selection_range(location, parent = nil)
87
100
  RubyLsp::Requests::Support::SelectionRange.new(
88
101
  range: LanguageServer::Protocol::Interface::Range.new(
@@ -1,8 +1,10 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
+ # ![Semantic highlighting demo](../../misc/semantic_highlighting.gif)
7
+ #
6
8
  # The [semantic
7
9
  # highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
8
10
  # request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
@@ -17,12 +19,35 @@ module RubyLsp
17
19
  # end
18
20
  # ```
19
21
  class SemanticHighlighting < BaseRequest
20
- TOKEN_TYPES = [
21
- :variable,
22
- :method,
23
- ].freeze
24
-
25
- TOKEN_MODIFIERS = {
22
+ extend T::Sig
23
+
24
+ TOKEN_TYPES = T.let({
25
+ namespace: 0,
26
+ type: 1,
27
+ class: 2,
28
+ enum: 3,
29
+ interface: 4,
30
+ struct: 5,
31
+ typeParameter: 6,
32
+ parameter: 7,
33
+ variable: 8,
34
+ property: 9,
35
+ enumMember: 10,
36
+ event: 11,
37
+ function: 12,
38
+ method: 13,
39
+ macro: 14,
40
+ keyword: 15,
41
+ modifier: 16,
42
+ comment: 17,
43
+ string: 18,
44
+ number: 19,
45
+ regexp: 20,
46
+ operator: 21,
47
+ decorator: 22,
48
+ }.freeze, T::Hash[Symbol, Integer])
49
+
50
+ TOKEN_MODIFIERS = T.let({
26
51
  declaration: 0,
27
52
  definition: 1,
28
53
  readonly: 2,
@@ -33,18 +58,38 @@ module RubyLsp
33
58
  modification: 7,
34
59
  documentation: 8,
35
60
  default_library: 9,
36
- }.freeze
37
-
38
- SemanticToken = Struct.new(:location, :length, :type, :modifier)
61
+ }.freeze, T::Hash[Symbol, Integer])
62
+
63
+ SPECIAL_RUBY_METHODS = T.let((Module.instance_methods(false) +
64
+ Kernel.methods(false) + Bundler::Dsl.instance_methods(false) +
65
+ Module.private_instance_methods(false))
66
+ .map(&:to_s), T::Array[String])
67
+
68
+ class SemanticToken < T::Struct
69
+ const :location, SyntaxTree::Location
70
+ const :length, Integer
71
+ const :type, Integer
72
+ const :modifier, T::Array[Integer]
73
+ end
39
74
 
75
+ sig { params(document: Document, encoder: T.nilable(Support::SemanticTokenEncoder)).void }
40
76
  def initialize(document, encoder: nil)
41
77
  super(document)
42
78
 
43
79
  @encoder = encoder
44
- @tokens = []
45
- @tree = document.tree
80
+ @tokens = T.let([], T::Array[SemanticToken])
81
+ @tree = T.let(T.must(document.tree), SyntaxTree::Node)
82
+ @special_methods = T.let(nil, T.nilable(T::Array[String]))
46
83
  end
47
84
 
85
+ sig do
86
+ override.returns(
87
+ T.any(
88
+ LanguageServer::Protocol::Interface::SemanticTokens,
89
+ T.all(T::Array[SemanticToken], Object),
90
+ )
91
+ )
92
+ end
48
93
  def run
49
94
  visit(@tree)
50
95
  return @tokens unless @encoder
@@ -52,60 +97,158 @@ module RubyLsp
52
97
  @encoder.encode(@tokens)
53
98
  end
54
99
 
55
- def visit_m_assign(node)
56
- node.target.parts.each do |var_ref|
57
- add_token(var_ref.value.location, :variable)
58
- end
59
- end
60
-
61
- def visit_var_field(node)
62
- case node.value
63
- when SyntaxTree::Ident
64
- add_token(node.value.location, :variable)
65
- end
66
- end
67
-
68
- def visit_var_ref(node)
69
- case node.value
70
- when SyntaxTree::Ident
71
- add_token(node.value.location, :variable)
72
- end
73
- end
74
-
100
+ sig { params(node: SyntaxTree::ARefField).void }
75
101
  def visit_a_ref_field(node)
76
102
  add_token(node.collection.value.location, :variable)
77
103
  end
78
104
 
105
+ sig { params(node: SyntaxTree::Call).void }
79
106
  def visit_call(node)
80
107
  visit(node.receiver)
81
108
  add_token(node.message.location, :method)
82
109
  visit(node.arguments)
83
110
  end
84
111
 
112
+ sig { params(node: SyntaxTree::Command).void }
85
113
  def visit_command(node)
86
- add_token(node.message.location, :method)
114
+ add_token(node.message.location, :method) unless special_method?(node.message.value)
87
115
  visit(node.arguments)
88
116
  end
89
117
 
118
+ sig { params(node: SyntaxTree::CommandCall).void }
90
119
  def visit_command_call(node)
91
120
  visit(node.receiver)
92
121
  add_token(node.message.location, :method)
93
122
  visit(node.arguments)
94
123
  end
95
124
 
125
+ sig { params(node: SyntaxTree::Const).void }
126
+ def visit_const(node)
127
+ add_token(node.location, :namespace)
128
+ end
129
+
130
+ sig { params(node: SyntaxTree::Def).void }
131
+ def visit_def(node)
132
+ add_token(node.name.location, :method, [:declaration])
133
+ visit(node.params)
134
+ visit(node.bodystmt)
135
+ end
136
+
137
+ sig { params(node: SyntaxTree::DefEndless).void }
138
+ def visit_def_endless(node)
139
+ add_token(node.name.location, :method, [:declaration])
140
+ visit(node.paren)
141
+ visit(node.operator)
142
+ visit(node.statement)
143
+ end
144
+
145
+ sig { params(node: SyntaxTree::Defs).void }
146
+ def visit_defs(node)
147
+ visit(node.target)
148
+ visit(node.operator)
149
+ add_token(node.name.location, :method, [:declaration])
150
+ visit(node.params)
151
+ visit(node.bodystmt)
152
+ end
153
+
154
+ sig { params(node: SyntaxTree::FCall).void }
96
155
  def visit_fcall(node)
97
- add_token(node.value.location, :method)
156
+ add_token(node.value.location, :method) unless special_method?(node.value.value)
98
157
  visit(node.arguments)
99
158
  end
100
159
 
160
+ sig { params(node: SyntaxTree::Kw).void }
161
+ def visit_kw(node)
162
+ case node.value
163
+ when "self"
164
+ add_token(node.location, :variable, [:default_library])
165
+ end
166
+ end
167
+
168
+ sig { params(node: SyntaxTree::MAssign).void }
169
+ def visit_m_assign(node)
170
+ node.target.parts.each do |var_ref|
171
+ add_token(var_ref.value.location, :variable)
172
+ end
173
+ end
174
+
175
+ sig { params(node: SyntaxTree::Params).void }
176
+ def visit_params(node)
177
+ node.keywords.each do |keyword,|
178
+ location = keyword.location
179
+ add_token(location_without_colon(location), :variable)
180
+ end
181
+
182
+ rest = node.keyword_rest
183
+ return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
184
+
185
+ name = rest.name
186
+ add_token(name.location, :variable) if name
187
+ end
188
+
189
+ sig { params(node: SyntaxTree::VarField).void }
190
+ def visit_var_field(node)
191
+ case node.value
192
+ when SyntaxTree::Ident
193
+ add_token(node.value.location, :variable)
194
+ else
195
+ visit(node.value)
196
+ end
197
+ end
198
+
199
+ sig { params(node: SyntaxTree::VarRef).void }
200
+ def visit_var_ref(node)
201
+ case node.value
202
+ when SyntaxTree::Ident
203
+ add_token(node.value.location, :variable)
204
+ else
205
+ visit(node.value)
206
+ end
207
+ end
208
+
209
+ sig { params(node: SyntaxTree::VCall).void }
101
210
  def visit_vcall(node)
102
- add_token(node.value.location, :method)
211
+ add_token(node.value.location, :method) unless special_method?(node.value.value)
103
212
  end
104
213
 
214
+ sig { params(location: SyntaxTree::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
105
215
  def add_token(location, type, modifiers = [])
106
216
  length = location.end_char - location.start_char
107
217
  modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
108
- @tokens.push(SemanticToken.new(location, length, TOKEN_TYPES.index(type), modifiers_indices))
218
+ @tokens.push(
219
+ SemanticToken.new(
220
+ location: location,
221
+ length: length,
222
+ type: T.must(TOKEN_TYPES[type]),
223
+ modifier: modifiers_indices
224
+ )
225
+ )
226
+ end
227
+
228
+ private
229
+
230
+ # Exclude the ":" symbol at the end of a location
231
+ # We use it on keyword parameters to be consistent
232
+ # with the rest of the parameters
233
+ sig { params(location: T.untyped).returns(SyntaxTree::Location) }
234
+ def location_without_colon(location)
235
+ SyntaxTree::Location.new(
236
+ start_line: location.start_line,
237
+ start_column: location.start_column,
238
+ start_char: location.start_char,
239
+ end_char: location.end_char - 1,
240
+ end_column: location.end_column - 1,
241
+ end_line: location.end_line,
242
+ )
243
+ end
244
+
245
+ # Textmate provides highlighting for a subset
246
+ # of these special Ruby-specific methods.
247
+ # We want to utilize that highlighting, so we
248
+ # avoid making a semantic token for it.
249
+ sig { params(method_name: String).returns(T::Boolean) }
250
+ def special_method?(method_name)
251
+ SPECIAL_RUBY_METHODS.include?(method_name)
109
252
  end
110
253
  end
111
254
  end
@@ -0,0 +1,87 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ module Support
7
+ class HighlightTarget
8
+ extend T::Sig
9
+
10
+ READ = LanguageServer::Protocol::Constant::DocumentHighlightKind::READ
11
+ WRITE = LanguageServer::Protocol::Constant::DocumentHighlightKind::WRITE
12
+
13
+ class HighlightMatch < T::Struct
14
+ const :type, Integer
15
+ const :node, SyntaxTree::Node
16
+ end
17
+
18
+ sig { params(node: SyntaxTree::Node).void }
19
+ def initialize(node)
20
+ @node = node
21
+ @value = T.let(value(node), T.nilable(String))
22
+ end
23
+
24
+ sig { params(other: SyntaxTree::Node).returns(T.nilable(HighlightMatch)) }
25
+ def highlight_type(other)
26
+ matched_highlight(other) if other.is_a?(SyntaxTree::Params) || (@value && @value == value(other))
27
+ end
28
+
29
+ private
30
+
31
+ # Match the target type (where the cursor is positioned) with the `other` type (the node we're currently
32
+ # visiting)
33
+ sig { params(other: SyntaxTree::Node).returns(T.nilable(HighlightMatch)) }
34
+ def matched_highlight(other)
35
+ case @node
36
+ # Method definitions and invocations
37
+ when SyntaxTree::VCall, SyntaxTree::FCall, SyntaxTree::Call, SyntaxTree::Command,
38
+ SyntaxTree::CommandCall, SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::DefEndless
39
+ case other
40
+ when SyntaxTree::VCall, SyntaxTree::FCall, SyntaxTree::Call, SyntaxTree::Command, SyntaxTree::CommandCall
41
+ HighlightMatch.new(type: READ, node: other)
42
+ when SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::Defs
43
+ HighlightMatch.new(type: WRITE, node: other.name)
44
+ end
45
+ # Variables, parameters and constants
46
+ when SyntaxTree::GVar, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar, SyntaxTree::VarField,
47
+ SyntaxTree::VarRef, SyntaxTree::Ident
48
+ case other
49
+ when SyntaxTree::VarField
50
+ HighlightMatch.new(type: WRITE, node: other)
51
+ when SyntaxTree::VarRef
52
+ HighlightMatch.new(type: READ, node: other)
53
+ when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
54
+ HighlightMatch.new(type: WRITE, node: other.constant)
55
+ when SyntaxTree::ConstPathRef
56
+ HighlightMatch.new(type: READ, node: other.constant)
57
+ when SyntaxTree::Params
58
+ params = other.child_nodes.compact
59
+ match = params.find { |param| value(param) == @value }
60
+ HighlightMatch.new(type: WRITE, node: match) if match
61
+ end
62
+ end
63
+ end
64
+
65
+ sig { params(node: SyntaxTree::Node).returns(T.nilable(String)) }
66
+ def value(node)
67
+ case node
68
+ when SyntaxTree::ConstPathRef, SyntaxTree::ConstPathField, SyntaxTree::TopConstField
69
+ node.constant.value
70
+ when SyntaxTree::GVar, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar, SyntaxTree::Ident,
71
+ SyntaxTree::ArgsForward
72
+ node.value
73
+ when SyntaxTree::Field, SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::DefEndless, SyntaxTree::RestParam,
74
+ SyntaxTree::KwRestParam, SyntaxTree::BlockArg
75
+ node.name&.value
76
+ when SyntaxTree::VarField, SyntaxTree::VarRef, SyntaxTree::VCall, SyntaxTree::FCall
77
+ node.value&.value
78
+ when SyntaxTree::Call, SyntaxTree::Command, SyntaxTree::CommandCall
79
+ node.message.value
80
+ when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
81
+ node.constant.constant.value
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,35 +1,45 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
6
  module Support
7
7
  class RuboCopDiagnostic
8
- RUBOCOP_TO_LSP_SEVERITY = {
8
+ extend T::Sig
9
+
10
+ RUBOCOP_TO_LSP_SEVERITY = T.let({
9
11
  convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
10
12
  info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
11
13
  refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
12
14
  warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
13
15
  error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
14
16
  fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
15
- }.freeze
17
+ }.freeze, T::Hash[Symbol, Integer])
16
18
 
19
+ sig { returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
17
20
  attr_reader :replacements
18
21
 
22
+ sig { params(offense: RuboCop::Cop::Offense, uri: String).void }
19
23
  def initialize(offense, uri)
20
24
  @offense = offense
21
25
  @uri = uri
22
- @replacements = offense.correctable? ? offense_replacements : []
26
+ @replacements = T.let(
27
+ offense.correctable? ? offense_replacements : [],
28
+ T::Array[LanguageServer::Protocol::Interface::TextEdit]
29
+ )
23
30
  end
24
31
 
32
+ sig { returns(T::Boolean) }
25
33
  def correctable?
26
34
  @offense.correctable?
27
35
  end
28
36
 
37
+ sig { params(range: T::Range[Integer]).returns(T::Boolean) }
29
38
  def in_range?(range)
30
39
  range.cover?(@offense.line - 1)
31
40
  end
32
41
 
42
+ sig { returns(LanguageServer::Protocol::Interface::CodeAction) }
33
43
  def to_lsp_code_action
34
44
  LanguageServer::Protocol::Interface::CodeAction.new(
35
45
  title: "Autocorrect #{@offense.cop_name}",
@@ -49,6 +59,7 @@ module RubyLsp
49
59
  )
50
60
  end
51
61
 
62
+ sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
52
63
  def to_lsp_diagnostic
53
64
  LanguageServer::Protocol::Interface::Diagnostic.new(
54
65
  message: @offense.message,
@@ -70,6 +81,7 @@ module RubyLsp
70
81
 
71
82
  private
72
83
 
84
+ sig { returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
73
85
  def offense_replacements
74
86
  @offense.corrector.as_replacements.map do |range, replacement|
75
87
  LanguageServer::Protocol::Interface::TextEdit.new(
@@ -0,0 +1,61 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "rubocop"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ require "cgi"
11
+ require "singleton"
12
+
13
+ module RubyLsp
14
+ module Requests
15
+ module Support
16
+ # :nodoc:
17
+ class RuboCopDiagnosticsRunner < RuboCop::Runner
18
+ extend T::Sig
19
+ include Singleton
20
+
21
+ sig { void }
22
+ 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
+ )
36
+ end
37
+
38
+ sig { params(uri: String, document: Document).returns(T::Array[Support::RuboCopDiagnostic]) }
39
+ 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
+
47
+ # Invoke RuboCop with just this file in `paths`
48
+ super([file])
49
+ @diagnostics
50
+ end
51
+
52
+ private
53
+
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)) }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,50 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "rubocop"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ require "cgi"
11
+ require "singleton"
12
+
13
+ module RubyLsp
14
+ module Requests
15
+ module Support
16
+ # :nodoc:
17
+ class RuboCopFormattingRunner < RuboCop::Runner
18
+ extend T::Sig
19
+ include Singleton
20
+
21
+ sig { void }
22
+ 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
+ )
35
+ end
36
+
37
+ sig { params(uri: String, document: Document).returns(T.nilable(String)) }
38
+ def run(uri, document)
39
+ file = CGI.unescape(URI.parse(uri).path)
40
+ # We communicate with Rubocop via stdin
41
+ @options[:stdin] = document.source
42
+
43
+ # Invoke RuboCop with just this file in `paths`
44
+ super([file])
45
+ @options[:stdin]
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,10 +1,13 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
6
  module Support
7
7
  class SelectionRange < LanguageServer::Protocol::Interface::SelectionRange
8
+ extend T::Sig
9
+
10
+ sig { params(position: Document::PositionShape).returns(T::Boolean) }
8
11
  def cover?(position)
9
12
  line_range = (range.start.line..range.end.line)
10
13
  character_range = (range.start.character..range.end.character)
@@ -1,15 +1,23 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
6
  module Support
7
7
  class SemanticTokenEncoder
8
+ extend T::Sig
9
+
10
+ sig { void }
8
11
  def initialize
9
- @current_row = 0
10
- @current_column = 0
12
+ @current_row = T.let(0, Integer)
13
+ @current_column = T.let(0, Integer)
11
14
  end
12
15
 
16
+ sig do
17
+ params(
18
+ tokens: T::Array[SemanticHighlighting::SemanticToken]
19
+ ).returns(LanguageServer::Protocol::Interface::SemanticTokens)
20
+ end
13
21
  def encode(tokens)
14
22
  delta = tokens
15
23
  .sort_by do |token|
@@ -31,6 +39,7 @@ module RubyLsp
31
39
 
32
40
  # For more information on how each number is calculated, read:
33
41
  # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
42
+ sig { params(token: SemanticHighlighting::SemanticToken).returns(T::Array[Integer]) }
34
43
  def compute_delta(token)
35
44
  row = token.location.start_line - 1
36
45
  column = token.location.start_column
@@ -49,6 +58,7 @@ module RubyLsp
49
58
  # For example, [:default_library] will be encoded as
50
59
  # 0b1000000000, as :default_library is the 10th bit according
51
60
  # to the token modifiers index map.
61
+ sig { params(modifiers: T::Array[Integer]).returns(Integer) }
52
62
  def encode_modifiers(modifiers)
53
63
  modifiers.inject(0) do |encoded_modifiers, modifier|
54
64
  encoded_modifiers | (1 << modifier)
@@ -1,18 +1,23 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
6
  module Support
7
7
  class SyntaxErrorDiagnostic
8
+ extend T::Sig
9
+
10
+ sig { params(edit: Document::EditShape).void }
8
11
  def initialize(edit)
9
12
  @edit = edit
10
13
  end
11
14
 
15
+ sig { returns(FalseClass) }
12
16
  def correctable?
13
17
  false
14
18
  end
15
19
 
20
+ sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
16
21
  def to_lsp_diagnostic
17
22
  LanguageServer::Protocol::Interface::Diagnostic.new(
18
23
  message: "Syntax error",