Class: RubyLsp::Requests::SemanticHighlighting

Inherits:
BaseRequest
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/ruby_lsp/requests/semantic_highlighting.rb

Overview

![Semantic highlighting demo](../../misc/semantic_highlighting.gif)

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.

# Example

“‘ruby def foo

var = 1 # --> semantic highlighting: local variable
some_invocation # --> semantic highlighting: method invocation
var # --> semantic highlighting: local variable

end “‘

Defined Under Namespace

Classes: SemanticToken

Constant Summary collapse

TOKEN_TYPES =
T.let({
  namespace: 0,
  type: 1,
  class: 2,
  enum: 3,
  interface: 4,
  struct: 5,
  typeParameter: 6,
  parameter: 7,
  variable: 8,
  property: 9,
  enumMember: 10,
  event: 11,
  function: 12,
  method: 13,
  macro: 14,
  keyword: 15,
  modifier: 16,
  comment: 17,
  string: 18,
  number: 19,
  regexp: 20,
  operator: 21,
  decorator: 22,
}.freeze, T::Hash[Symbol, Integer])
TOKEN_MODIFIERS =
T.let({
  declaration: 0,
  definition: 1,
  readonly: 2,
  static: 3,
  deprecated: 4,
  abstract: 5,
  async: 6,
  modification: 7,
  documentation: 8,
  default_library: 9,
}.freeze, T::Hash[Symbol, Integer])
SPECIAL_RUBY_METHODS =
T.let([
  Module.instance_methods(false),
  Kernel.instance_methods(false),
  Kernel.methods(false),
  Bundler::Dsl.instance_methods(false),
  Module.private_instance_methods(false),
].flatten.map(&:to_s), T::Array[String])

Instance Method Summary collapse

Methods inherited from BaseRequest

#range_from_syntax_tree_node

Constructor Details

#initialize(document, encoder: nil) ⇒ SemanticHighlighting

Returns a new instance of SemanticHighlighting.



79
80
81
82
83
84
85
86
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 79

def initialize(document, encoder: nil)
  super(document)

  @encoder = encoder
  @tokens = T.let([], T::Array[SemanticToken])
  @tree = T.let(T.must(document.tree), SyntaxTree::Node)
  @special_methods = T.let(nil, T.nilable(T::Array[String]))
end

Instance Method Details

#add_token(location, type, modifiers = []) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 221

def add_token(location, type, modifiers = [])
  length = location.end_char - location.start_char
  modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
  @tokens.push(
    SemanticToken.new(
      location: location,
      length: length,
      type: T.must(TOKEN_TYPES[type]),
      modifier: modifiers_indices
    )
  )
end

#runObject



96
97
98
99
100
101
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 96

def run
  visit(@tree)
  return @tokens unless @encoder

  @encoder.encode(@tokens)
end

#visit_a_ref_field(node) ⇒ Object



104
105
106
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 104

def visit_a_ref_field(node)
  add_token(node.collection.value.location, :variable)
end

#visit_call(node) ⇒ Object



109
110
111
112
113
114
115
116
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 109

def visit_call(node)
  visit(node.receiver)

  message = node.message
  add_token(message.location, :method) if message != :call

  visit(node.arguments)
end

#visit_command(node) ⇒ Object



119
120
121
122
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 119

def visit_command(node)
  add_token(node.message.location, :method) unless special_method?(node.message.value)
  visit(node.arguments)
end

#visit_command_call(node) ⇒ Object



125
126
127
128
129
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 125

def visit_command_call(node)
  visit(node.receiver)
  add_token(node.message.location, :method)
  visit(node.arguments)
end

#visit_const(node) ⇒ Object



132
133
134
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 132

def visit_const(node)
  add_token(node.location, :namespace)
end

#visit_def(node) ⇒ Object



137
138
139
140
141
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 137

def visit_def(node)
  add_token(node.name.location, :method, [:declaration])
  visit(node.params)
  visit(node.bodystmt)
end

#visit_def_endless(node) ⇒ Object



144
145
146
147
148
149
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 144

def visit_def_endless(node)
  add_token(node.name.location, :method, [:declaration])
  visit(node.paren)
  visit(node.operator)
  visit(node.statement)
end

#visit_defs(node) ⇒ Object



152
153
154
155
156
157
158
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 152

def visit_defs(node)
  visit(node.target)
  visit(node.operator)
  add_token(node.name.location, :method, [:declaration])
  visit(node.params)
  visit(node.bodystmt)
end

#visit_fcall(node) ⇒ Object



161
162
163
164
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 161

def visit_fcall(node)
  add_token(node.value.location, :method) unless special_method?(node.value.value)
  visit(node.arguments)
end

#visit_kw(node) ⇒ Object



167
168
169
170
171
172
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 167

def visit_kw(node)
  case node.value
  when "self"
    add_token(node.location, :variable, [:default_library])
  end
end

#visit_m_assign(node) ⇒ Object



175
176
177
178
179
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 175

def visit_m_assign(node)
  node.target.parts.each do |var_ref|
    add_token(var_ref.value.location, :variable)
  end
end

#visit_params(node) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 182

def visit_params(node)
  node.keywords.each do |keyword,|
    location = keyword.location
    add_token(location_without_colon(location), :variable)
  end

  rest = node.keyword_rest
  return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)

  name = rest.name
  add_token(name.location, :variable) if name
end

#visit_var_field(node) ⇒ Object



196
197
198
199
200
201
202
203
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 196

def visit_var_field(node)
  case node.value
  when SyntaxTree::Ident
    add_token(node.value.location, :variable)
  else
    visit(node.value)
  end
end

#visit_var_ref(node) ⇒ Object



206
207
208
209
210
211
212
213
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 206

def visit_var_ref(node)
  case node.value
  when SyntaxTree::Ident
    add_token(node.value.location, :variable)
  else
    visit(node.value)
  end
end

#visit_vcall(node) ⇒ Object



216
217
218
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 216

def visit_vcall(node)
  add_token(node.value.location, :method) unless special_method?(node.value.value)
end