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



226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 226

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_call(node) ⇒ Object



104
105
106
107
108
109
110
111
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 104

def visit_call(node)
  visit(node.receiver)

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

  visit(node.arguments)
end

#visit_class(node) ⇒ Object



213
214
215
216
217
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 213

def visit_class(node)
  add_token(node.constant.location, :class, [:declaration])
  add_token(node.superclass.location, :class) if node.superclass
  visit(node.bodystmt)
end

#visit_command(node) ⇒ Object



114
115
116
117
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 114

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



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

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

#visit_const(node) ⇒ Object



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

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

#visit_def(node) ⇒ Object



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

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

#visit_def_endless(node) ⇒ Object



139
140
141
142
143
144
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 139

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



147
148
149
150
151
152
153
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 147

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



156
157
158
159
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 156

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

#visit_kw(node) ⇒ Object



162
163
164
165
166
167
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 162

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

#visit_module(node) ⇒ Object



220
221
222
223
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 220

def visit_module(node)
  add_token(node.constant.location, :class, [:declaration])
  visit(node.bodystmt)
end

#visit_params(node) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 170

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

  node.requireds.each do |required|
    add_token(required.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



188
189
190
191
192
193
194
195
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 188

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



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

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



208
209
210
# File 'lib/ruby_lsp/requests/semantic_highlighting.rb', line 208

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