ruby-lsp 0.13.3 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/VERSION +1 -1
  4. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +4 -8
  5. data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +5 -1
  6. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +4 -2
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +8 -3
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +9 -0
  9. data/lib/ruby_indexer/test/index_test.rb +27 -0
  10. data/lib/ruby_lsp/addon.rb +21 -10
  11. data/lib/ruby_lsp/check_docs.rb +8 -8
  12. data/lib/ruby_lsp/executor.rb +28 -10
  13. data/lib/ruby_lsp/internal.rb +1 -1
  14. data/lib/ruby_lsp/listeners/code_lens.rb +54 -55
  15. data/lib/ruby_lsp/listeners/completion.rb +17 -16
  16. data/lib/ruby_lsp/listeners/definition.rb +10 -16
  17. data/lib/ruby_lsp/listeners/document_highlight.rb +6 -11
  18. data/lib/ruby_lsp/listeners/document_link.rb +6 -12
  19. data/lib/ruby_lsp/listeners/document_symbol.rb +95 -55
  20. data/lib/ruby_lsp/listeners/folding_ranges.rb +19 -23
  21. data/lib/ruby_lsp/listeners/hover.rb +26 -30
  22. data/lib/ruby_lsp/listeners/inlay_hints.rb +7 -13
  23. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -124
  24. data/lib/ruby_lsp/listeners/signature_help.rb +11 -13
  25. data/lib/ruby_lsp/requests/code_lens.rb +9 -17
  26. data/lib/ruby_lsp/requests/completion.rb +7 -9
  27. data/lib/ruby_lsp/requests/definition.rb +10 -22
  28. data/lib/ruby_lsp/requests/document_highlight.rb +7 -5
  29. data/lib/ruby_lsp/requests/document_link.rb +7 -6
  30. data/lib/ruby_lsp/requests/document_symbol.rb +5 -11
  31. data/lib/ruby_lsp/requests/folding_ranges.rb +11 -6
  32. data/lib/ruby_lsp/requests/hover.rb +18 -24
  33. data/lib/ruby_lsp/requests/inlay_hints.rb +7 -8
  34. data/lib/ruby_lsp/requests/on_type_formatting.rb +12 -2
  35. data/lib/ruby_lsp/requests/semantic_highlighting.rb +10 -8
  36. data/lib/ruby_lsp/requests/signature_help.rb +53 -18
  37. data/lib/ruby_lsp/requests/support/common.rb +23 -10
  38. data/lib/ruby_lsp/requests/support/dependency_detector.rb +5 -1
  39. data/lib/ruby_lsp/requests.rb +0 -1
  40. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +29 -0
  41. data/lib/ruby_lsp/response_builders/document_symbol.rb +57 -0
  42. data/lib/ruby_lsp/response_builders/hover.rb +49 -0
  43. data/lib/ruby_lsp/response_builders/response_builder.rb +16 -0
  44. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +199 -0
  45. data/lib/ruby_lsp/response_builders/signature_help.rb +28 -0
  46. data/lib/ruby_lsp/response_builders.rb +13 -0
  47. data/lib/ruby_lsp/server.rb +3 -3
  48. data/lib/ruby_lsp/setup_bundler.rb +30 -5
  49. data/lib/ruby_lsp/store.rb +4 -4
  50. metadata +14 -9
  51. data/lib/ruby_lsp/listener.rb +0 -33
  52. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +0 -73
@@ -0,0 +1,199 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module ResponseBuilders
6
+ class SemanticHighlighting < ResponseBuilder
7
+ class UndefinedTokenType < StandardError; end
8
+
9
+ TOKEN_TYPES = T.let(
10
+ {
11
+ namespace: 0,
12
+ type: 1,
13
+ class: 2,
14
+ enum: 3,
15
+ interface: 4,
16
+ struct: 5,
17
+ typeParameter: 6,
18
+ parameter: 7,
19
+ variable: 8,
20
+ property: 9,
21
+ enumMember: 10,
22
+ event: 11,
23
+ function: 12,
24
+ method: 13,
25
+ macro: 14,
26
+ keyword: 15,
27
+ modifier: 16,
28
+ comment: 17,
29
+ string: 18,
30
+ number: 19,
31
+ regexp: 20,
32
+ operator: 21,
33
+ decorator: 22,
34
+ }.freeze,
35
+ T::Hash[Symbol, Integer],
36
+ )
37
+
38
+ TOKEN_MODIFIERS = T.let(
39
+ {
40
+ declaration: 0,
41
+ definition: 1,
42
+ readonly: 2,
43
+ static: 3,
44
+ deprecated: 4,
45
+ abstract: 5,
46
+ async: 6,
47
+ modification: 7,
48
+ documentation: 8,
49
+ default_library: 9,
50
+ }.freeze,
51
+ T::Hash[Symbol, Integer],
52
+ )
53
+
54
+ extend T::Sig
55
+
56
+ ResponseType = type_member { { fixed: Interface::SemanticTokens } }
57
+
58
+ sig { void }
59
+ def initialize
60
+ super
61
+ @stack = T.let([], T::Array[SemanticToken])
62
+ end
63
+
64
+ sig { params(location: Prism::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
65
+ def add_token(location, type, modifiers = [])
66
+ length = location.end_offset - location.start_offset
67
+ modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
68
+ @stack.push(
69
+ SemanticToken.new(
70
+ location: location,
71
+ length: length,
72
+ type: T.must(TOKEN_TYPES[type]),
73
+ modifier: modifiers_indices,
74
+ ),
75
+ )
76
+ end
77
+
78
+ sig { returns(T.nilable(SemanticToken)) }
79
+ def last
80
+ @stack.last
81
+ end
82
+
83
+ sig { override.returns(Interface::SemanticTokens) }
84
+ def response
85
+ SemanticTokenEncoder.new.encode(@stack)
86
+ end
87
+
88
+ class SemanticToken
89
+ extend T::Sig
90
+
91
+ sig { returns(Prism::Location) }
92
+ attr_reader :location
93
+
94
+ sig { returns(Integer) }
95
+ attr_reader :length
96
+
97
+ sig { returns(Integer) }
98
+ attr_reader :type
99
+
100
+ sig { returns(T::Array[Integer]) }
101
+ attr_reader :modifier
102
+
103
+ sig { params(location: Prism::Location, length: Integer, type: Integer, modifier: T::Array[Integer]).void }
104
+ def initialize(location:, length:, type:, modifier:)
105
+ @location = location
106
+ @length = length
107
+ @type = type
108
+ @modifier = modifier
109
+ end
110
+
111
+ sig { params(type_symbol: Symbol).void }
112
+ def replace_type(type_symbol)
113
+ type_int = TOKEN_TYPES[type_symbol]
114
+ raise UndefinedTokenType, "Undefined token type: #{type_symbol}" unless type_int
115
+
116
+ @type = type_int
117
+ end
118
+
119
+ sig { params(modifier_symbols: T::Array[Symbol]).void }
120
+ def replace_modifier(modifier_symbols)
121
+ @modifier = modifier_symbols.filter_map do |modifier_symbol|
122
+ modifier_index = TOKEN_MODIFIERS[modifier_symbol]
123
+ raise UndefinedTokenType, "Undefined token modifier: #{modifier_symbol}" unless modifier_index
124
+
125
+ modifier_index
126
+ end
127
+ end
128
+ end
129
+
130
+ class SemanticTokenEncoder
131
+ extend T::Sig
132
+
133
+ sig { void }
134
+ def initialize
135
+ @current_row = T.let(0, Integer)
136
+ @current_column = T.let(0, Integer)
137
+ end
138
+
139
+ sig do
140
+ params(
141
+ tokens: T::Array[SemanticToken],
142
+ ).returns(Interface::SemanticTokens)
143
+ end
144
+ def encode(tokens)
145
+ sorted_tokens = tokens.sort_by.with_index do |token, index|
146
+ # Enumerable#sort_by is not deterministic when the compared values are equal.
147
+ # When that happens, we need to use the index as a tie breaker to ensure
148
+ # that the order of the tokens is always the same.
149
+ [token.location.start_line, token.location.start_column, index]
150
+ end
151
+
152
+ delta = sorted_tokens.flat_map do |token|
153
+ compute_delta(token)
154
+ end
155
+
156
+ Interface::SemanticTokens.new(data: delta)
157
+ end
158
+
159
+ # The delta array is computed according to the LSP specification:
160
+ # > The protocol for the token format relative uses relative
161
+ # > positions, because most tokens remain stable relative to
162
+ # > each other when edits are made in a file. This simplifies
163
+ # > the computation of a delta if a server supports it. So each
164
+ # > token is represented using 5 integers.
165
+
166
+ # For more information on how each number is calculated, read:
167
+ # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
168
+ sig { params(token: SemanticToken).returns(T::Array[Integer]) }
169
+ def compute_delta(token)
170
+ row = token.location.start_line - 1
171
+ column = token.location.start_column
172
+
173
+ begin
174
+ delta_line = row - @current_row
175
+
176
+ delta_column = column
177
+ delta_column -= @current_column if delta_line == 0
178
+
179
+ [delta_line, delta_column, token.length, token.type, encode_modifiers(token.modifier)]
180
+ ensure
181
+ @current_row = row
182
+ @current_column = column
183
+ end
184
+ end
185
+
186
+ # Encode an array of modifiers to positions onto a bit flag
187
+ # For example, [:default_library] will be encoded as
188
+ # 0b1000000000, as :default_library is the 10th bit according
189
+ # to the token modifiers index map.
190
+ sig { params(modifiers: T::Array[Integer]).returns(Integer) }
191
+ def encode_modifiers(modifiers)
192
+ modifiers.inject(0) do |encoded_modifiers, modifier|
193
+ encoded_modifiers | (1 << modifier)
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,28 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module ResponseBuilders
6
+ class SignatureHelp < ResponseBuilder
7
+ ResponseType = type_member { { fixed: T.nilable(Interface::SignatureHelp) } }
8
+
9
+ extend T::Sig
10
+
11
+ sig { void }
12
+ def initialize
13
+ super
14
+ @signature_help = T.let(nil, ResponseType)
15
+ end
16
+
17
+ sig { params(signature_help: ResponseType).void }
18
+ def replace(signature_help)
19
+ @signature_help = signature_help
20
+ end
21
+
22
+ sig { override.returns(ResponseType) }
23
+ def response
24
+ @signature_help
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module ResponseBuilders
6
+ autoload :CollectionResponseBuilder, "ruby_lsp/response_builders/collection_response_builder"
7
+ autoload :DocumentSymbol, "ruby_lsp/response_builders/document_symbol"
8
+ autoload :Hover, "ruby_lsp/response_builders/hover"
9
+ autoload :ResponseBuilder, "ruby_lsp/response_builders/response_builder"
10
+ autoload :SemanticHighlighting, "ruby_lsp/response_builders/semantic_highlighting"
11
+ autoload :SignatureHelp, "ruby_lsp/response_builders/signature_help"
12
+ end
13
+ end
@@ -58,7 +58,7 @@ module RubyLsp
58
58
 
59
59
  sig { void }
60
60
  def start
61
- warn("Starting Ruby LSP v#{VERSION}...")
61
+ $stderr.puts("Starting Ruby LSP v#{VERSION}...")
62
62
 
63
63
  # Requests that have to be executed sequentially or in the main process are implemented here. All other requests
64
64
  # fall under the else branch which just pushes requests to the queue
@@ -73,7 +73,7 @@ module RubyLsp
73
73
  when "$/setTrace"
74
74
  VOID
75
75
  when "shutdown"
76
- warn("Shutting down Ruby LSP...")
76
+ $stderr.puts("Shutting down Ruby LSP...")
77
77
 
78
78
  @message_queue.close
79
79
  # Close the queue so that we can no longer receive items
@@ -92,7 +92,7 @@ module RubyLsp
92
92
  # We return zero if shutdown has already been received or one otherwise as per the recommendation in the spec
93
93
  # https://microsoft.github.io/language-server-protocol/specification/#exit
94
94
  status = @store.empty? ? 0 : 1
95
- warn("Shutdown complete with status #{status}")
95
+ $stderr.puts("Shutdown complete with status #{status}")
96
96
  exit(status)
97
97
  else
98
98
  # Default case: push the request to the queue to be executed by the worker
@@ -59,7 +59,9 @@ module RubyLsp
59
59
 
60
60
  # Do not setup a custom bundle if both `ruby-lsp` and `debug` are already in the Gemfile
61
61
  if @dependencies["ruby-lsp"] && @dependencies["debug"]
62
- warn("Ruby LSP> Skipping custom bundle setup since both `ruby-lsp` and `debug` are already in #{@gemfile}")
62
+ $stderr.puts(
63
+ "Ruby LSP> Skipping custom bundle setup since both `ruby-lsp` and `debug` are already in #{@gemfile}",
64
+ )
63
65
 
64
66
  # If the user decided to add the `ruby-lsp` and `debug` to their Gemfile after having already run the Ruby LSP,
65
67
  # then we need to remove the `.ruby-lsp` folder, otherwise we will run `bundle install` for the top level and
@@ -76,7 +78,7 @@ module RubyLsp
76
78
  write_custom_gemfile
77
79
 
78
80
  unless @gemfile&.exist? && @lockfile&.exist?
79
- warn("Ruby LSP> Skipping lockfile copies because there's no top level bundle")
81
+ $stderr.puts("Ruby LSP> Skipping lockfile copies because there's no top level bundle")
80
82
  return run_bundle_install(@custom_gemfile)
81
83
  end
82
84
 
@@ -84,11 +86,14 @@ module RubyLsp
84
86
  current_lockfile_hash = Digest::SHA256.hexdigest(lockfile_contents)
85
87
 
86
88
  if @custom_lockfile.exist? && @lockfile_hash_path.exist? && @lockfile_hash_path.read == current_lockfile_hash
87
- warn("Ruby LSP> Skipping custom bundle setup since #{@custom_lockfile} already exists and is up to date")
89
+ $stderr.puts(
90
+ "Ruby LSP> Skipping custom bundle setup since #{@custom_lockfile} already exists and is up to date",
91
+ )
88
92
  return run_bundle_install(@custom_gemfile)
89
93
  end
90
94
 
91
95
  FileUtils.cp(@lockfile.to_s, @custom_lockfile.to_s)
96
+ correct_relative_remote_paths
92
97
  @lockfile_hash_path.write(current_lockfile_hash)
93
98
  run_bundle_install(@custom_gemfile)
94
99
  end
@@ -208,8 +213,8 @@ module RubyLsp
208
213
  command << "1>&2"
209
214
 
210
215
  # Add bundle update
211
- warn("Ruby LSP> Running bundle install for the custom bundle. This may take a while...")
212
- warn("Ruby LSP> Command: #{command}")
216
+ $stderr.puts("Ruby LSP> Running bundle install for the custom bundle. This may take a while...")
217
+ $stderr.puts("Ruby LSP> Command: #{command}")
213
218
  system(env, command)
214
219
  [bundle_gemfile.to_s, expanded_path, env["BUNDLE_APP_CONFIG"]]
215
220
  end
@@ -227,5 +232,25 @@ module RubyLsp
227
232
  # If the last updated file doesn't exist or was updated more than 4 hours ago, we should update
228
233
  !@last_updated_path.exist? || Time.parse(@last_updated_path.read) < (Time.now - FOUR_HOURS)
229
234
  end
235
+
236
+ # When a lockfile has remote references based on relative file paths, we need to ensure that they are pointing to
237
+ # the correct place since after copying the relative path is no longer valid
238
+ sig { void }
239
+ def correct_relative_remote_paths
240
+ content = @custom_lockfile.read
241
+ content.gsub!(/remote: (.*)/) do |match|
242
+ path = T.must(Regexp.last_match)[1]
243
+
244
+ # We should only apply the correction if the remote is a relative path. It might also be a URI, like
245
+ # `https://rubygems.org` or an absolute path, in which case we shouldn't do anything
246
+ if path&.start_with?(".")
247
+ "remote: #{File.expand_path(path, T.must(@gemfile).dirname)}"
248
+ else
249
+ match
250
+ end
251
+ end
252
+
253
+ @custom_lockfile.write(content)
254
+ end
230
255
  end
231
256
  end
@@ -23,6 +23,9 @@ module RubyLsp
23
23
  sig { returns(T::Hash[Symbol, RequestConfig]) }
24
24
  attr_accessor :features_configuration
25
25
 
26
+ sig { returns(String) }
27
+ attr_accessor :client_name
28
+
26
29
  sig { void }
27
30
  def initialize
28
31
  @state = T.let({}, T::Hash[String, Document])
@@ -33,10 +36,6 @@ module RubyLsp
33
36
  @workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
34
37
  @features_configuration = T.let(
35
38
  {
36
- codeLens: RequestConfig.new({
37
- enableAll: false,
38
- gemfileLinks: true,
39
- }),
40
39
  inlayHint: RequestConfig.new({
41
40
  enableAll: false,
42
41
  implicitRescue: false,
@@ -45,6 +44,7 @@ module RubyLsp
45
44
  },
46
45
  T::Hash[Symbol, RequestConfig],
47
46
  )
47
+ @client_name = T.let("Unknown", String)
48
48
  end
49
49
 
50
50
  sig { params(uri: URI::Generic).returns(Document) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.3
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-11 00:00:00.000000000 Z
11
+ date: 2024-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: 0.19.0
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
- version: '0.20'
36
+ version: '0.22'
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -43,21 +43,21 @@ dependencies:
43
43
  version: 0.19.0
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.20'
46
+ version: '0.22'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sorbet-runtime
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: 0.5.5685
53
+ version: 0.5.10782
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 0.5.5685
60
+ version: 0.5.10782
61
61
  description: An opinionated language server for Ruby
62
62
  email:
63
63
  - ruby@shopify.com
@@ -97,7 +97,6 @@ files:
97
97
  - lib/ruby_lsp/document.rb
98
98
  - lib/ruby_lsp/executor.rb
99
99
  - lib/ruby_lsp/internal.rb
100
- - lib/ruby_lsp/listener.rb
101
100
  - lib/ruby_lsp/listeners/code_lens.rb
102
101
  - lib/ruby_lsp/listeners/completion.rb
103
102
  - lib/ruby_lsp/listeners/definition.rb
@@ -139,11 +138,17 @@ files:
139
138
  - lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb
140
139
  - lib/ruby_lsp/requests/support/rubocop_runner.rb
141
140
  - lib/ruby_lsp/requests/support/selection_range.rb
142
- - lib/ruby_lsp/requests/support/semantic_token_encoder.rb
143
141
  - lib/ruby_lsp/requests/support/sorbet.rb
144
142
  - lib/ruby_lsp/requests/support/source_uri.rb
145
143
  - lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb
146
144
  - lib/ruby_lsp/requests/workspace_symbol.rb
145
+ - lib/ruby_lsp/response_builders.rb
146
+ - lib/ruby_lsp/response_builders/collection_response_builder.rb
147
+ - lib/ruby_lsp/response_builders/document_symbol.rb
148
+ - lib/ruby_lsp/response_builders/hover.rb
149
+ - lib/ruby_lsp/response_builders/response_builder.rb
150
+ - lib/ruby_lsp/response_builders/semantic_highlighting.rb
151
+ - lib/ruby_lsp/response_builders/signature_help.rb
147
152
  - lib/ruby_lsp/ruby_document.rb
148
153
  - lib/ruby_lsp/server.rb
149
154
  - lib/ruby_lsp/setup_bundler.rb
@@ -169,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
174
  - !ruby/object:Gem::Version
170
175
  version: '0'
171
176
  requirements: []
172
- rubygems_version: 3.5.4
177
+ rubygems_version: 3.5.6
173
178
  signing_key:
174
179
  specification_version: 4
175
180
  summary: An opinionated language server for Ruby
@@ -1,33 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module RubyLsp
5
- # Listener is an abstract class to be used by requests for listening to events emitted when visiting an AST using the
6
- # Prism::Dispatcher.
7
- class Listener
8
- extend T::Sig
9
- extend T::Helpers
10
- extend T::Generic
11
- include Requests::Support::Common
12
-
13
- ResponseType = type_member
14
-
15
- abstract!
16
-
17
- sig { params(dispatcher: Prism::Dispatcher).void }
18
- def initialize(dispatcher)
19
- super()
20
- @dispatcher = dispatcher
21
- end
22
-
23
- sig { returns(ResponseType) }
24
- def response
25
- _response
26
- end
27
-
28
- # Override this method with an attr_reader that returns the response of your listener. The listener should
29
- # accumulate results in a @response variable and then provide the reader so that it is accessible
30
- sig { abstract.returns(ResponseType) }
31
- def _response; end
32
- end
33
- end
@@ -1,73 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module RubyLsp
5
- module Requests
6
- module Support
7
- class SemanticTokenEncoder
8
- extend T::Sig
9
-
10
- sig { void }
11
- def initialize
12
- @current_row = T.let(0, Integer)
13
- @current_column = T.let(0, Integer)
14
- end
15
-
16
- sig do
17
- params(
18
- tokens: T::Array[Listeners::SemanticHighlighting::SemanticToken],
19
- ).returns(Interface::SemanticTokens)
20
- end
21
- def encode(tokens)
22
- delta = tokens
23
- .sort_by do |token|
24
- [token.location.start_line, token.location.start_column]
25
- end
26
- .flat_map do |token|
27
- compute_delta(token)
28
- end
29
-
30
- Interface::SemanticTokens.new(data: delta)
31
- end
32
-
33
- # The delta array is computed according to the LSP specification:
34
- # > The protocol for the token format relative uses relative
35
- # > positions, because most tokens remain stable relative to
36
- # > each other when edits are made in a file. This simplifies
37
- # > the computation of a delta if a server supports it. So each
38
- # > token is represented using 5 integers.
39
-
40
- # For more information on how each number is calculated, read:
41
- # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
42
- sig { params(token: Listeners::SemanticHighlighting::SemanticToken).returns(T::Array[Integer]) }
43
- def compute_delta(token)
44
- row = token.location.start_line - 1
45
- column = token.location.start_column
46
-
47
- begin
48
- delta_line = row - @current_row
49
-
50
- delta_column = column
51
- delta_column -= @current_column if delta_line == 0
52
-
53
- [delta_line, delta_column, token.length, token.type, encode_modifiers(token.modifier)]
54
- ensure
55
- @current_row = row
56
- @current_column = column
57
- end
58
- end
59
-
60
- # Encode an array of modifiers to positions onto a bit flag
61
- # For example, [:default_library] will be encoded as
62
- # 0b1000000000, as :default_library is the 10th bit according
63
- # to the token modifiers index map.
64
- sig { params(modifiers: T::Array[Integer]).returns(Integer) }
65
- def encode_modifiers(modifiers)
66
- modifiers.inject(0) do |encoded_modifiers, modifier|
67
- encoded_modifiers | (1 << modifier)
68
- end
69
- end
70
- end
71
- end
72
- end
73
- end