Class: RubyLsp::Requests::SemanticHighlighting
- Defined in:
- lib/ruby_lsp/requests/semantic_highlighting.rb
Overview
The [semantic highlighting](microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens) request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
Class Method Summary collapse
-
.compute_delta(current_tokens, previous_tokens, result_id) ⇒ Object
The compute_delta method receives the current semantic tokens and the previous semantic tokens and then tries to compute the smallest possible semantic token edit that will turn previous into current : (Array current_tokens, Array previous_tokens, String result_id) -> Interface::SemanticTokensDelta.
-
.next_result_id ⇒ Object
: -> Integer.
-
.provider ⇒ Object
: -> Interface::SemanticTokensRegistrationOptions.
Instance Method Summary collapse
-
#initialize(global_state, dispatcher, document, previous_result_id, range: nil) ⇒ SemanticHighlighting
constructor
: (GlobalState global_state, Prism::Dispatcher dispatcher, (RubyDocument | ERBDocument) document, String? previous_result_id, ?range: Range?) -> void.
-
#perform ⇒ Object
: -> (Interface::SemanticTokens | Interface::SemanticTokensDelta).
Constructor Details
#initialize(global_state, dispatcher, document, previous_result_id, range: nil) ⇒ SemanticHighlighting
: (GlobalState global_state, Prism::Dispatcher dispatcher, (RubyDocument | ERBDocument) document, String? previous_result_id, ?range: Range?) -> void
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 78 def initialize(global_state, dispatcher, document, previous_result_id, range: nil) super() @document = document @previous_result_id = previous_result_id @range = range @result_id = SemanticHighlighting.next_result_id.to_s #: String @response_builder = ResponseBuilders::SemanticHighlighting .new(document.code_units_cache) #: ResponseBuilders::SemanticHighlighting Listeners::SemanticHighlighting.new(dispatcher, @response_builder) Addon.addons.each do |addon| addon.create_semantic_highlighting_listener(@response_builder, dispatcher) end end |
Class Method Details
.compute_delta(current_tokens, previous_tokens, result_id) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 29 def compute_delta(current_tokens, previous_tokens, result_id) # Find the index of the first token that is different between the two sets of tokens first_different_position = current_tokens.zip(previous_tokens).find_index { |new, old| new != old } # When deleting a token from the end, the first_different_position will be nil, but since we're removing at # the end, then we have to initialize it to the length of the current tokens after the deletion if !first_different_position && current_tokens.length < previous_tokens.length first_different_position = current_tokens.length end unless first_different_position return Interface::SemanticTokensDelta.new(result_id: result_id, edits: []) end # Filter the tokens based on the first different position. This must happen at this stage, before we try to # find the next position from the end or else we risk confusing sets of token that may have different lengths, # but end with the exact same token old_tokens = previous_tokens[first_different_position...] #: as !nil new_tokens = current_tokens[first_different_position...] #: as !nil # Then search from the end to find the first token that doesn't match. Since the user is normally editing the # middle of the file, this will minimize the number of edits since the end of the token array has not changed first_different_token_from_end = new_tokens.reverse.zip(old_tokens.reverse).find_index do |new, old| new != old end || 0 # Filter the old and new tokens to only the section that will be replaced/inserted/deleted old_tokens = old_tokens[...old_tokens.length - first_different_token_from_end] #: as !nil new_tokens = new_tokens[...new_tokens.length - first_different_token_from_end] #: as !nil # And we send back a single edit, replacing an entire section for the new tokens Interface::SemanticTokensDelta.new( result_id: result_id, edits: [{ start: first_different_position, deleteCount: old_tokens.length, data: new_tokens }], ) end |
.next_result_id ⇒ Object
: -> Integer
67 68 69 70 71 |
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 67 def next_result_id @mutex.synchronize do @result_id += 1 end end |
.provider ⇒ Object
: -> Interface::SemanticTokensRegistrationOptions
14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 14 def provider Interface::SemanticTokensRegistrationOptions.new( document_selector: nil, legend: Interface::SemanticTokensLegend.new( token_types: ResponseBuilders::SemanticHighlighting::TOKEN_TYPES.keys, token_modifiers: ResponseBuilders::SemanticHighlighting::TOKEN_MODIFIERS.keys, ), range: true, full: { delta: true }, ) end |
Instance Method Details
#perform ⇒ Object
: -> (Interface::SemanticTokens | Interface::SemanticTokensDelta)
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 96 def perform previous_tokens = @document.semantic_tokens tokens = @response_builder.response encoded_tokens = ResponseBuilders::SemanticHighlighting::SemanticTokenEncoder.new.encode(tokens) full_response = Interface::SemanticTokens.new(result_id: @result_id, data: encoded_tokens) @document.semantic_tokens = full_response if @range tokens_within_range = tokens.select { |token| @range.cover?(token.start_line - 1) } return Interface::SemanticTokens.new( result_id: @result_id, data: ResponseBuilders::SemanticHighlighting::SemanticTokenEncoder.new.encode(tokens_within_range), ) end # Semantic tokens full delta if @previous_result_id response = if previous_tokens.is_a?(Interface::SemanticTokens) && previous_tokens.result_id == @previous_result_id Requests::SemanticHighlighting.compute_delta(encoded_tokens, previous_tokens.data, @result_id) else full_response end return response end # Semantic tokens full full_response end |