ruby-lsp 0.0.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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