ruby-lsp 0.0.3 → 0.2.0

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +6 -0
  3. data/.rubocop.yml +25 -0
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile +10 -6
  6. data/Gemfile.lock +63 -16
  7. data/README.md +41 -0
  8. data/Rakefile +8 -1
  9. data/VERSION +1 -1
  10. data/bin/console +19 -0
  11. data/bin/tapioca +29 -0
  12. data/dev.yml +3 -0
  13. data/exe/ruby-lsp +19 -4
  14. data/lib/ruby-lsp.rb +2 -1
  15. data/lib/ruby_lsp/cli.rb +13 -5
  16. data/lib/ruby_lsp/document.rb +43 -14
  17. data/lib/ruby_lsp/handler.rb +107 -37
  18. data/lib/ruby_lsp/internal.rb +7 -0
  19. data/lib/ruby_lsp/requests/base_request.rb +18 -5
  20. data/lib/ruby_lsp/requests/code_actions.rb +20 -8
  21. data/lib/ruby_lsp/requests/diagnostics.rb +25 -7
  22. data/lib/ruby_lsp/requests/document_highlight.rb +113 -0
  23. data/lib/ruby_lsp/requests/document_symbol.rb +56 -16
  24. data/lib/ruby_lsp/requests/folding_ranges.rb +70 -34
  25. data/lib/ruby_lsp/requests/formatting.rb +24 -14
  26. data/lib/ruby_lsp/requests/selection_ranges.rb +18 -4
  27. data/lib/ruby_lsp/requests/semantic_highlighting.rb +187 -34
  28. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +16 -3
  29. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +61 -0
  30. data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +50 -0
  31. data/lib/ruby_lsp/requests/support/selection_range.rb +4 -0
  32. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +24 -3
  33. data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +6 -0
  34. data/lib/ruby_lsp/requests.rb +13 -1
  35. data/lib/ruby_lsp/store.rb +20 -3
  36. data/rakelib/check_docs.rake +34 -6
  37. data/ruby-lsp.gemspec +7 -5
  38. data/sorbet/config +4 -0
  39. data/sorbet/rbi/.rubocop.yml +8 -0
  40. data/sorbet/rbi/gems/ansi@1.5.0.rbi +338 -0
  41. data/sorbet/rbi/gems/ast@2.4.2.rbi +522 -0
  42. data/sorbet/rbi/gems/builder@3.2.4.rbi +418 -0
  43. data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
  44. data/sorbet/rbi/gems/debug@1.5.0.rbi +1273 -0
  45. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +867 -0
  46. data/sorbet/rbi/gems/io-console@0.5.11.rbi +8 -0
  47. data/sorbet/rbi/gems/irb@1.4.1.rbi +376 -0
  48. data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +7325 -0
  49. data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
  50. data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +612 -0
  51. data/sorbet/rbi/gems/minitest@5.15.0.rbi +994 -0
  52. data/sorbet/rbi/gems/parallel@1.22.1.rbi +163 -0
  53. data/sorbet/rbi/gems/parser@3.1.2.0.rbi +3968 -0
  54. data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +734 -0
  55. data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
  56. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +227 -0
  57. data/sorbet/rbi/gems/rake@13.0.6.rbi +1853 -0
  58. data/sorbet/rbi/gems/rbi@0.0.14.rbi +2337 -0
  59. data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +1854 -0
  60. data/sorbet/rbi/gems/reline@0.3.1.rbi +1274 -0
  61. data/sorbet/rbi/gems/rexml@3.2.5.rbi +3852 -0
  62. data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +4180 -0
  63. data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +1369 -0
  64. data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +246 -0
  65. data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +8 -0
  66. data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +652 -0
  67. data/sorbet/rbi/gems/rubocop@1.30.0.rbi +36729 -0
  68. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +732 -0
  69. data/sorbet/rbi/gems/spoom@1.1.11.rbi +1600 -0
  70. data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +6777 -0
  71. data/sorbet/rbi/gems/tapioca@0.8.1.rbi +1972 -0
  72. data/sorbet/rbi/gems/thor@1.2.1.rbi +2921 -0
  73. data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +27 -0
  74. data/sorbet/rbi/gems/unparser@0.6.5.rbi +2789 -0
  75. data/sorbet/rbi/gems/webrick@1.7.0.rbi +1779 -0
  76. data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +289 -0
  77. data/sorbet/rbi/gems/yard@0.9.27.rbi +13048 -0
  78. data/sorbet/rbi/shims/fiddle.rbi +4 -0
  79. data/sorbet/rbi/shims/hash.rbi +6 -0
  80. data/sorbet/rbi/shims/rdoc.rbi +4 -0
  81. data/sorbet/tapioca/config.yml +13 -0
  82. data/sorbet/tapioca/require.rb +7 -0
  83. metadata +62 -13
  84. data/lib/ruby_lsp/requests/rubocop_request.rb +0 -49
  85. data/shipit.production.yml +0 -1
@@ -1,7 +1,10 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
4
5
  module Requests
6
+ # ![Semantic highlighting demo](../../misc/semantic_highlighting.gif)
7
+ #
5
8
  # The [semantic
6
9
  # highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
7
10
  # request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
@@ -16,22 +19,77 @@ module RubyLsp
16
19
  # end
17
20
  # ```
18
21
  class SemanticHighlighting < BaseRequest
19
- TOKEN_TYPES = [
20
- :variable,
21
- :method,
22
- ].freeze
23
- TOKEN_MODIFIERS = [].freeze
24
-
25
- SemanticToken = Struct.new(:location, :length, :type, :modifier)
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({
51
+ declaration: 0,
52
+ definition: 1,
53
+ readonly: 2,
54
+ static: 3,
55
+ deprecated: 4,
56
+ abstract: 5,
57
+ async: 6,
58
+ modification: 7,
59
+ documentation: 8,
60
+ default_library: 9,
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
26
74
 
75
+ sig { params(document: Document, encoder: T.nilable(Support::SemanticTokenEncoder)).void }
27
76
  def initialize(document, encoder: nil)
28
77
  super(document)
29
78
 
30
79
  @encoder = encoder
31
- @tokens = []
32
- @tree = document.tree
80
+ @tokens = T.let([], T::Array[SemanticToken])
81
+ @tree = T.let(document.tree, SyntaxTree::Node)
82
+ @special_methods = T.let(nil, T.nilable(T::Array[String]))
33
83
  end
34
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
35
93
  def run
36
94
  visit(@tree)
37
95
  return @tokens unless @encoder
@@ -39,59 +97,154 @@ module RubyLsp
39
97
  @encoder.encode(@tokens)
40
98
  end
41
99
 
42
- def visit_m_assign(node)
43
- node.target.parts.each do |var_ref|
44
- add_token(var_ref.value.location, :variable)
45
- end
46
- end
47
-
48
- def visit_var_field(node)
49
- case node.value
50
- when SyntaxTree::Ident
51
- add_token(node.value.location, :variable)
52
- end
53
- end
54
-
55
- def visit_var_ref(node)
56
- case node.value
57
- when SyntaxTree::Ident
58
- add_token(node.value.location, :variable)
59
- end
60
- end
61
-
100
+ sig { params(node: SyntaxTree::ARefField).void }
62
101
  def visit_a_ref_field(node)
63
102
  add_token(node.collection.value.location, :variable)
64
103
  end
65
104
 
105
+ sig { params(node: SyntaxTree::Call).void }
66
106
  def visit_call(node)
67
107
  visit(node.receiver)
68
108
  add_token(node.message.location, :method)
69
109
  visit(node.arguments)
70
110
  end
71
111
 
112
+ sig { params(node: SyntaxTree::Command).void }
72
113
  def visit_command(node)
73
- add_token(node.message.location, :method)
114
+ add_token(node.message.location, :method) unless special_method?(node.message.value)
74
115
  visit(node.arguments)
75
116
  end
76
117
 
118
+ sig { params(node: SyntaxTree::CommandCall).void }
77
119
  def visit_command_call(node)
78
120
  visit(node.receiver)
79
121
  add_token(node.message.location, :method)
80
122
  visit(node.arguments)
81
123
  end
82
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 }
83
155
  def visit_fcall(node)
84
- add_token(node.value.location, :method)
156
+ add_token(node.value.location, :method) unless special_method?(node.value.value)
85
157
  visit(node.arguments)
86
158
  end
87
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
+ add_token(node.keyword_rest.name.location, :variable) if node.keyword_rest
183
+ end
184
+
185
+ sig { params(node: SyntaxTree::VarField).void }
186
+ def visit_var_field(node)
187
+ case node.value
188
+ when SyntaxTree::Ident
189
+ add_token(node.value.location, :variable)
190
+ else
191
+ visit(node.value)
192
+ end
193
+ end
194
+
195
+ sig { params(node: SyntaxTree::VarRef).void }
196
+ def visit_var_ref(node)
197
+ case node.value
198
+ when SyntaxTree::Ident
199
+ add_token(node.value.location, :variable)
200
+ else
201
+ visit(node.value)
202
+ end
203
+ end
204
+
205
+ sig { params(node: SyntaxTree::VCall).void }
88
206
  def visit_vcall(node)
89
- add_token(node.value.location, :method)
207
+ add_token(node.value.location, :method) unless special_method?(node.value.value)
90
208
  end
91
209
 
92
- def add_token(location, type)
210
+ sig { params(location: SyntaxTree::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
211
+ def add_token(location, type, modifiers = [])
93
212
  length = location.end_char - location.start_char
94
- @tokens.push(SemanticToken.new(location, length, TOKEN_TYPES.index(type), 0))
213
+ modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
214
+ @tokens.push(
215
+ SemanticToken.new(
216
+ location: location,
217
+ length: length,
218
+ type: T.must(TOKEN_TYPES[type]),
219
+ modifier: modifiers_indices
220
+ )
221
+ )
222
+ end
223
+
224
+ private
225
+
226
+ # Exclude the ":" symbol at the end of a location
227
+ # We use it on keyword parameters to be consistent
228
+ # with the rest of the parameters
229
+ sig { params(location: T.untyped).returns(SyntaxTree::Location) }
230
+ def location_without_colon(location)
231
+ SyntaxTree::Location.new(
232
+ start_line: location.start_line,
233
+ start_column: location.start_column,
234
+ start_char: location.start_char,
235
+ end_char: location.end_char - 1,
236
+ end_column: location.end_column - 1,
237
+ end_line: location.end_line,
238
+ )
239
+ end
240
+
241
+ # Textmate provides highlighting for a subset
242
+ # of these special Ruby-specific methods.
243
+ # We want to utilize that highlighting, so we
244
+ # avoid making a semantic token for it.
245
+ sig { params(method_name: String).returns(T::Boolean) }
246
+ def special_method?(method_name)
247
+ SPECIAL_RUBY_METHODS.include?(method_name)
95
248
  end
96
249
  end
97
250
  end
@@ -1,34 +1,45 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
4
5
  module Requests
5
6
  module Support
6
7
  class RuboCopDiagnostic
7
- RUBOCOP_TO_LSP_SEVERITY = {
8
+ extend T::Sig
9
+
10
+ RUBOCOP_TO_LSP_SEVERITY = T.let({
8
11
  convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
9
12
  info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
10
13
  refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
11
14
  warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
12
15
  error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
13
16
  fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
14
- }.freeze
17
+ }.freeze, T::Hash[Symbol, Integer])
15
18
 
19
+ sig { returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
16
20
  attr_reader :replacements
17
21
 
22
+ sig { params(offense: RuboCop::Cop::Offense, uri: String).void }
18
23
  def initialize(offense, uri)
19
24
  @offense = offense
20
25
  @uri = uri
21
- @replacements = offense.correctable? ? offense_replacements : []
26
+ @replacements = T.let(
27
+ offense.correctable? ? offense_replacements : [],
28
+ T::Array[LanguageServer::Protocol::Interface::TextEdit]
29
+ )
22
30
  end
23
31
 
32
+ sig { returns(T::Boolean) }
24
33
  def correctable?
25
34
  @offense.correctable?
26
35
  end
27
36
 
37
+ sig { params(range: T::Range[Integer]).returns(T::Boolean) }
28
38
  def in_range?(range)
29
39
  range.cover?(@offense.line - 1)
30
40
  end
31
41
 
42
+ sig { returns(LanguageServer::Protocol::Interface::CodeAction) }
32
43
  def to_lsp_code_action
33
44
  LanguageServer::Protocol::Interface::CodeAction.new(
34
45
  title: "Autocorrect #{@offense.cop_name}",
@@ -48,6 +59,7 @@ module RubyLsp
48
59
  )
49
60
  end
50
61
 
62
+ sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
51
63
  def to_lsp_diagnostic
52
64
  LanguageServer::Protocol::Interface::Diagnostic.new(
53
65
  message: @offense.message,
@@ -69,6 +81,7 @@ module RubyLsp
69
81
 
70
82
  private
71
83
 
84
+ sig { returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
72
85
  def offense_replacements
73
86
  @offense.corrector.as_replacements.map do |range, replacement|
74
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,9 +1,13 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
4
5
  module Requests
5
6
  module Support
6
7
  class SelectionRange < LanguageServer::Protocol::Interface::SelectionRange
8
+ extend T::Sig
9
+
10
+ sig { params(position: Document::PositionShape).returns(T::Boolean) }
7
11
  def cover?(position)
8
12
  line_range = (range.start.line..range.end.line)
9
13
  character_range = (range.start.character..range.end.character)
@@ -1,14 +1,23 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
4
5
  module Requests
5
6
  module Support
6
7
  class SemanticTokenEncoder
8
+ extend T::Sig
9
+
10
+ sig { void }
7
11
  def initialize
8
- @current_row = 0
9
- @current_column = 0
12
+ @current_row = T.let(0, Integer)
13
+ @current_column = T.let(0, Integer)
10
14
  end
11
15
 
16
+ sig do
17
+ params(
18
+ tokens: T::Array[SemanticHighlighting::SemanticToken]
19
+ ).returns(LanguageServer::Protocol::Interface::SemanticTokens)
20
+ end
12
21
  def encode(tokens)
13
22
  delta = tokens
14
23
  .sort_by do |token|
@@ -30,6 +39,7 @@ module RubyLsp
30
39
 
31
40
  # For more information on how each number is calculated, read:
32
41
  # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
42
+ sig { params(token: SemanticHighlighting::SemanticToken).returns(T::Array[Integer]) }
33
43
  def compute_delta(token)
34
44
  row = token.location.start_line - 1
35
45
  column = token.location.start_column
@@ -38,11 +48,22 @@ module RubyLsp
38
48
  delta_column = column
39
49
  delta_column -= @current_column if delta_line == 0
40
50
 
41
- [delta_line, delta_column, token.length, token.type, token.modifier]
51
+ [delta_line, delta_column, token.length, token.type, encode_modifiers(token.modifier)]
42
52
  ensure
43
53
  @current_row = row
44
54
  @current_column = column
45
55
  end
56
+
57
+ # Encode an array of modifiers to positions onto a bit flag
58
+ # For example, [:default_library] will be encoded as
59
+ # 0b1000000000, as :default_library is the 10th bit according
60
+ # to the token modifiers index map.
61
+ sig { params(modifiers: T::Array[Integer]).returns(Integer) }
62
+ def encode_modifiers(modifiers)
63
+ modifiers.inject(0) do |encoded_modifiers, modifier|
64
+ encoded_modifiers | (1 << modifier)
65
+ end
66
+ end
46
67
  end
47
68
  end
48
69
  end
@@ -1,17 +1,23 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
4
5
  module Requests
5
6
  module Support
6
7
  class SyntaxErrorDiagnostic
8
+ extend T::Sig
9
+
10
+ sig { params(edit: Document::EditShape).void }
7
11
  def initialize(edit)
8
12
  @edit = edit
9
13
  end
10
14
 
15
+ sig { returns(FalseClass) }
11
16
  def correctable?
12
17
  false
13
18
  end
14
19
 
20
+ sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
15
21
  def to_lsp_diagnostic
16
22
  LanguageServer::Protocol::Interface::Diagnostic.new(
17
23
  message: "Syntax error",
@@ -1,17 +1,29 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
5
+ # Supported features
6
+ #
7
+ # - {RubyLsp::Requests::DocumentSymbol}
8
+ # - {RubyLsp::Requests::FoldingRanges}
9
+ # - {RubyLsp::Requests::SelectionRanges}
10
+ # - {RubyLsp::Requests::SemanticHighlighting}
11
+ # - {RubyLsp::Requests::Formatting}
12
+ # - {RubyLsp::Requests::Diagnostics}
13
+ # - {RubyLsp::Requests::CodeActions}
14
+ # - {RubyLsp::Requests::DocumentHighlight}
4
15
  module Requests
5
16
  autoload :BaseRequest, "ruby_lsp/requests/base_request"
6
17
  autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
7
18
  autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges"
8
19
  autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
9
20
  autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
10
- autoload :RuboCopRequest, "ruby_lsp/requests/rubocop_request"
11
21
  autoload :Formatting, "ruby_lsp/requests/formatting"
12
22
  autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
13
23
  autoload :CodeActions, "ruby_lsp/requests/code_actions"
24
+ autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
14
25
 
26
+ # :nodoc:
15
27
  module Support
16
28
  autoload :RuboCopDiagnostic, "ruby_lsp/requests/support/rubocop_diagnostic"
17
29
  autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require "cgi"
@@ -6,36 +7,52 @@ require "ruby_lsp/document"
6
7
 
7
8
  module RubyLsp
8
9
  class Store
10
+ extend T::Sig
11
+
12
+ sig { void }
9
13
  def initialize
10
- @state = {}
14
+ @state = T.let({}, T::Hash[String, Document])
11
15
  end
12
16
 
17
+ sig { params(uri: String).returns(Document) }
13
18
  def get(uri)
14
19
  document = @state[uri]
15
20
  return document unless document.nil?
16
21
 
17
22
  set(uri, File.binread(CGI.unescape(URI.parse(uri).path)))
18
- @state[uri]
23
+ T.must(@state[uri])
19
24
  end
20
25
 
26
+ sig { params(uri: String, content: String).void }
21
27
  def set(uri, content)
22
28
  @state[uri] = Document.new(content)
23
29
  rescue SyntaxTree::Parser::ParseError
24
30
  # Do not update the store if there are syntax errors
25
31
  end
26
32
 
33
+ sig { params(uri: String, edits: T::Array[Document::EditShape]).void }
27
34
  def push_edits(uri, edits)
28
- @state[uri].push_edits(edits)
35
+ T.must(@state[uri]).push_edits(edits)
29
36
  end
30
37
 
38
+ sig { void }
31
39
  def clear
32
40
  @state.clear
33
41
  end
34
42
 
43
+ sig { params(uri: String).void }
35
44
  def delete(uri)
36
45
  @state.delete(uri)
37
46
  end
38
47
 
48
+ sig do
49
+ type_parameters(:T)
50
+ .params(
51
+ uri: String,
52
+ request_name: Symbol,
53
+ block: T.proc.params(document: Document).returns(T.type_parameter(:T))
54
+ ).returns(T.type_parameter(:T))
55
+ end
39
56
  def cache_fetch(uri, request_name, &block)
40
57
  get(uri).cache_fetch(request_name, &block)
41
58
  end