ruby-lsp 0.0.4 → 0.2.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.
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",