# frozen_string_literal: true module RubyLsp module Requests class SemanticHighlighting < BaseRequest TOKEN_TYPES = [ :variable, :method, ].freeze TOKEN_MODIFIERS = [].freeze def initialize(document) super @tokens = [] @tree = document.tree @current_row = 0 @current_column = 0 end def run visit(@tree) LanguageServer::Protocol::Interface::SemanticTokens.new(data: @tokens) end def visit_m_assign(node) node.target.parts.each do |var_ref| add_token(var_ref.value.location, :variable) end end def visit_var_field(node) case node.value when SyntaxTree::Ident add_token(node.value.location, :variable) end end def visit_var_ref(node) case node.value when SyntaxTree::Ident add_token(node.value.location, :variable) end end def visit_a_ref_field(node) add_token(node.collection.value.location, :variable) end def visit_call(node) visit(node.receiver) add_token(node.message.location, :method) visit(node.arguments) end def visit_command(node) add_token(node.message.location, :method) visit(node.arguments) end def visit_command_call(node) visit(node.receiver) add_token(node.message.location, :method) visit(node.arguments) end def visit_fcall(node) add_token(node.value.location, :method) visit(node.arguments) end def visit_vcall(node) add_token(node.value.location, :method) end def add_token(location, classification) length = location.end_char - location.start_char compute_delta(location) do |delta_line, delta_column| @tokens.push(delta_line, delta_column, length, TOKEN_TYPES.index(classification), 0) end end # The delta array is computed according to the LSP specification: # > The protocol for the token format relative uses relative # > positions, because most tokens remain stable relative to # > each other when edits are made in a file. This simplifies # > the computation of a delta if a server supports it. So each # > token is represented using 5 integers. # For more information on how each number is calculated, read: # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens def compute_delta(location) row = location.start_line - 1 column = location.start_column if row < @current_row raise InvalidTokenRowError, "Invalid token row detected: " \ "Ensure tokens are added in the expected order." end delta_line = row - @current_row delta_column = column delta_column -= @current_column if delta_line == 0 yield delta_line, delta_column @current_row = row @current_column = column end class InvalidTokenRowError < StandardError; end end end end