Class: RubyLsp::Requests::DocumentSymbol

Inherits:
ExtensibleListener
  • Object
show all
Extended by:
T::Generic, T::Sig
Defined in:
lib/ruby_lsp/requests/document_symbol.rb

Overview

![Document symbol demo](../../document_symbol.gif)

The [document symbol](microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.

In VS Code, fuzzy symbol search can be accessed by opening the command palette and inserting an ‘@` symbol.

# Example

“‘ruby class Person # –> document symbol: class

attr_reader :age # --> document symbol: field

def initialize
  @age = 0 # --> document symbol: variable
end

def age # --> document symbol: method
end

end “‘

Defined Under Namespace

Classes: SymbolHierarchyRoot

Constant Summary collapse

ResponseType =
type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
ATTR_ACCESSORS =
T.let([:attr_reader, :attr_writer, :attr_accessor].freeze, T::Array[Symbol])

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Listener

#response

Methods included from Support::Common

#create_code_lens, #markdown_from_index_entries, #range_from_location, #range_from_node, #visible?

Constructor Details

#initialize(dispatcher, message_queue) ⇒ DocumentSymbol

Returns a new instance of DocumentSymbol.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 53

def initialize(dispatcher, message_queue)
  @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
  @_response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
  @stack = T.let(
    [@root],
    T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
  )

  super

  dispatcher.register(
    self,
    :on_class_node_enter,
    :on_class_node_leave,
    :on_call_node_enter,
    :on_constant_path_write_node_enter,
    :on_constant_write_node_enter,
    :on_def_node_enter,
    :on_def_node_leave,
    :on_module_node_enter,
    :on_module_node_leave,
    :on_instance_variable_write_node_enter,
    :on_class_variable_write_node_enter,
    :on_singleton_class_node_enter,
    :on_singleton_class_node_leave,
  )
end

Instance Attribute Details

#_responseObject (readonly)

Returns the value of attribute _response.



50
51
52
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 50

def _response
  @_response
end

Instance Method Details

#initialize_external_listener(addon) ⇒ Object



82
83
84
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 82

def initialize_external_listener(addon)
  addon.create_document_symbol_listener(@dispatcher, @message_queue)
end

#merge_response!(other) ⇒ Object



88
89
90
91
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 88

def merge_response!(other)
  @_response.concat(other.response)
  self
end

#on_call_node_enter(node) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 126

def on_call_node_enter(node)
  return unless ATTR_ACCESSORS.include?(node.name) && node.receiver.nil?

  arguments = node.arguments
  return unless arguments

  arguments.arguments.each do |argument|
    next unless argument.is_a?(Prism::SymbolNode)

    name = argument.value
    next unless name

    create_document_symbol(
      name: name,
      kind: Constant::SymbolKind::FIELD,
      range_location: argument.location,
      selection_range_location: T.must(argument.value_loc),
    )
  end
end

#on_class_node_enter(node) ⇒ Object



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

def on_class_node_enter(node)
  @stack << create_document_symbol(
    name: node.constant_path.location.slice,
    kind: Constant::SymbolKind::CLASS,
    range_location: node.location,
    selection_range_location: node.constant_path.location,
  )
end

#on_class_node_leave(node) ⇒ Object



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

def on_class_node_leave(node)
  @stack.pop
end

#on_class_variable_write_node_enter(node) ⇒ Object



224
225
226
227
228
229
230
231
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 224

def on_class_variable_write_node_enter(node)
  create_document_symbol(
    name: node.name.to_s,
    kind: Constant::SymbolKind::VARIABLE,
    range_location: node.name_loc,
    selection_range_location: node.name_loc,
  )
end

#on_constant_path_write_node_enter(node) ⇒ Object



148
149
150
151
152
153
154
155
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 148

def on_constant_path_write_node_enter(node)
  create_document_symbol(
    name: node.target.location.slice,
    kind: Constant::SymbolKind::CONSTANT,
    range_location: node.location,
    selection_range_location: node.target.location,
  )
end

#on_constant_write_node_enter(node) ⇒ Object



158
159
160
161
162
163
164
165
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 158

def on_constant_write_node_enter(node)
  create_document_symbol(
    name: node.name.to_s,
    kind: Constant::SymbolKind::CONSTANT,
    range_location: node.location,
    selection_range_location: node.name_loc,
  )
end

#on_def_node_enter(node) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 183

def on_def_node_enter(node)
  receiver = node.receiver
  previous_symbol = @stack.last

  if receiver.is_a?(Prism::SelfNode)
    name = "self.#{node.name}"
    kind = Constant::SymbolKind::FUNCTION
  elsif previous_symbol.is_a?(Interface::DocumentSymbol) && previous_symbol.name.start_with?("<<")
    name = node.name.to_s
    kind = Constant::SymbolKind::FUNCTION
  else
    name = node.name.to_s
    kind = name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
  end

  symbol = create_document_symbol(
    name: name,
    kind: kind,
    range_location: node.location,
    selection_range_location: node.name_loc,
  )

  @stack << symbol
end

#on_def_node_leave(node) ⇒ Object



168
169
170
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 168

def on_def_node_leave(node)
  @stack.pop
end

#on_instance_variable_write_node_enter(node) ⇒ Object



214
215
216
217
218
219
220
221
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 214

def on_instance_variable_write_node_enter(node)
  create_document_symbol(
    name: node.name.to_s,
    kind: Constant::SymbolKind::VARIABLE,
    range_location: node.name_loc,
    selection_range_location: node.name_loc,
  )
end

#on_module_node_enter(node) ⇒ Object



173
174
175
176
177
178
179
180
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 173

def on_module_node_enter(node)
  @stack << create_document_symbol(
    name: node.constant_path.location.slice,
    kind: Constant::SymbolKind::MODULE,
    range_location: node.location,
    selection_range_location: node.constant_path.location,
  )
end

#on_module_node_leave(node) ⇒ Object



209
210
211
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 209

def on_module_node_leave(node)
  @stack.pop
end

#on_singleton_class_node_enter(node) ⇒ Object



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

def on_singleton_class_node_enter(node)
  expression = node.expression

  @stack << create_document_symbol(
    name: "<< #{expression.slice}",
    kind: Constant::SymbolKind::NAMESPACE,
    range_location: node.location,
    selection_range_location: expression.location,
  )
end

#on_singleton_class_node_leave(node) ⇒ Object



121
122
123
# File 'lib/ruby_lsp/requests/document_symbol.rb', line 121

def on_singleton_class_node_leave(node)
  @stack.pop
end