Class: Ibex::LSP::SymbolIndexBuilder
- Inherits:
-
Object
- Object
- Ibex::LSP::SymbolIndexBuilder
- Defined in:
- lib/ibex/lsp/symbol_index_builder.rb,
sig/ibex/lsp/symbol_index_builder.rbs
Overview
Builds source occurrences and hover metadata from lossless frontend documents.
Instance Method Summary collapse
- #add_global_reference(path, token, name) ⇒ void
- #add_named_tokens(path, tokens, names, kind, role) ⇒ void
- #add_occurrence(**attributes) ⇒ void
- #build ⇒ [ Array[SymbolOccurrence], Hash[String, Frontend::SourceDocument] ]
- #canonical_include_target(declaration) ⇒ String?
- #collect_declaration(path, document, declaration, following) ⇒ void
- #collect_declaration_references(path, document, declaration, following) ⇒ void
- #collect_definitions(path, document) ⇒ void
- #collect_documents ⇒ void
- #collect_include(path, tokens, declaration) ⇒ void
- #collect_item(path, document, item, rule, rule_id) ⇒ void
- #collect_named_item(path, document, item, rule, rule_id) ⇒ void
- #collect_parameters(path, document, rule, lhs_token) ⇒ void
- #collect_precedence(declaration) ⇒ void
- #collect_references(path, document) ⇒ void
- #collect_rule_definition(path, document, rule) ⇒ void
- #collect_rule_references(path, document, rule, following_rule) ⇒ void
- #global_key(name) ⇒ Array[untyped]
-
#initialize(store, files) ⇒ SymbolIndexBuilder
constructor
A new instance of SymbolIndexBuilder.
- #parameter_key(path, rule_id, name) ⇒ Array[untyped]
- #parameter_signature(parameters) ⇒ String
- #symbol_data(kind, name) ⇒ Hash[Symbol, untyped]
Methods included from SymbolIndexPrecedenceReferences
#add_rule_reference, #collect_alternative_precedence
Methods included from SymbolIndexSourceQueries
#declaration_reference_names, #location_before?, #parameter_tokens, #token_at, #tokens_between
Constructor Details
#initialize(store, files) ⇒ SymbolIndexBuilder
Returns a new instance of SymbolIndexBuilder.
11 12 13 14 15 16 17 18 19 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 11 def initialize(store, files) @store = store @files = files @occurrences = [] #: Array[SymbolOccurrence] @documents = {} #: Hash[String, Frontend::SourceDocument] @global_kinds = {} #: Hash[String, Symbol] @rule_data = {} #: Hash[String, Hash[Symbol, untyped]] @terminal_data = Hash.new { |hash, key| hash[key] = {} } #: Hash[String, Hash[Symbol, untyped]] end |
Instance Method Details
#add_global_reference(path, token, name) ⇒ void
This method returns an undefined value.
207 208 209 210 211 212 213 214 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 207 def add_global_reference(path, token, name) return unless token.span kind = @global_kinds[name] || :terminal data = symbol_data(kind, name) add_occurrence(name: name, kind: kind, role: :reference, key: global_key(name), path: path, span: token.span, data: data) end |
#add_named_tokens(path, tokens, names, kind, role) ⇒ void
This method returns an undefined value.
224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 224 def add_named_tokens(path, tokens, names, kind, role) remaining = tokens.dup names.each do |name| index = remaining.index { |token| token.value == name } token = index && remaining.delete_at(index) next unless token&.span @global_kinds[name] ||= kind add_occurrence(name: name, kind: kind, role: role, key: global_key(name), path: path, span: token.span, data: @terminal_data[name]) end end |
#add_occurrence(**attributes) ⇒ void
This method returns an undefined value.
238 239 240 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 238 def add_occurrence(**attributes) @occurrences << SymbolOccurrence.new(**attributes) end |
#build ⇒ [ Array[SymbolOccurrence], Hash[String, Frontend::SourceDocument] ]
22 23 24 25 26 27 28 29 30 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 22 def build collect_documents # Definitions from every closure file must exist before references are classified. # rubocop:disable Style/CombinableLoops @documents.each { |path, document| collect_definitions(path, document) } @documents.each { |path, document| collect_references(path, document) } # rubocop:enable Style/CombinableLoops [@occurrences.freeze, @documents.freeze] end |
#canonical_include_target(declaration) ⇒ String?
258 259 260 261 262 263 264 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 258 def canonical_include_target(declaration) candidate = File.(declaration.path, File.dirname(declaration.loc.file)) target = @store.loader.canonical_path(candidate, allow_missing: true) @store.workspace.root_for(target) && target rescue SystemCallError nil end |
#collect_declaration(path, document, declaration, following) ⇒ void
This method returns an undefined value.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 58 def collect_declaration(path, document, declaration, following) tokens = tokens_between(document, declaration.loc, following) case declaration when Frontend::AST::Tokens add_named_tokens(path, tokens.drop(1), declaration.names, :terminal, :definition) when Frontend::AST::DisplayName @terminal_data[declaration.name][:display] = declaration.value when Frontend::AST::SemanticType @terminal_data[declaration.name][:type] = declaration.value when Frontend::AST::Precedence collect_precedence(declaration) when Frontend::AST::Include collect_include(path, tokens, declaration) end end |
#collect_declaration_references(path, document, declaration, following) ⇒ void
This method returns an undefined value.
154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 154 def collect_declaration_references(path, document, declaration, following) names = declaration_reference_names(declaration) return if names.empty? remaining = tokens_between(document, declaration.loc, following).drop(1) names.each do |name| index = remaining.index { |token| token.value == name } token = index && remaining.delete_at(index) add_global_reference(path, token, name) if token end end |
#collect_definitions(path, document) ⇒ void
This method returns an undefined value.
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 44 def collect_definitions(path, document) ast = document.ast return unless ast declarations = ast.declarations declarations.each_with_index do |declaration, index| following = declarations[index + 1]&.loc || ast.rules.first&.loc collect_declaration(path, document, declaration, following) end ast.rules.each { |rule| collect_rule_definition(path, document, rule) } end |
#collect_documents ⇒ void
This method returns an undefined value.
35 36 37 38 39 40 41 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 35 def collect_documents @files.each do |path| snapshot = @store.snapshot_for(path) document = snapshot&.fetch(:document) @documents[path] = document if document end end |
#collect_include(path, tokens, declaration) ⇒ void
This method returns an undefined value.
86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 86 def collect_include(path, tokens, declaration) token = tokens.find { |candidate| candidate.type == :literal } return unless token&.span target = canonical_include_target(declaration) return unless target add_occurrence( name: declaration.path, kind: :include, role: :reference, key: [:include, target], path: path, span: token.span, data: { target: target } ) end |
#collect_item(path, document, item, rule, rule_id) ⇒ void
This method returns an undefined value.
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 180 def collect_item(path, document, item, rule, rule_id) case item when Frontend::AST::SymbolReference, Frontend::AST::ParameterizedReference collect_named_item(path, document, item, rule, rule_id) item.arguments.each { |child| collect_item(path, document, child, rule, rule_id) } if item.is_a?(Frontend::AST::ParameterizedReference) when Frontend::AST::Optional, Frontend::AST::Star, Frontend::AST::Plus collect_item(path, document, item.item, rule, rule_id) when Frontend::AST::Group item.alternatives.flatten.each { |child| collect_item(path, document, child, rule, rule_id) } when Frontend::AST::SeparatedList collect_item(path, document, item.item, rule, rule_id) collect_item(path, document, item.separator, rule, rule_id) end end |
#collect_named_item(path, document, item, rule, rule_id) ⇒ void
This method returns an undefined value.
199 200 201 202 203 204 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 199 def collect_named_item(path, document, item, rule, rule_id) token = token_at(document, item.loc, item.name) return unless token&.span add_rule_reference(path, token, item.name, rule, rule_id) end |
#collect_parameters(path, document, rule, lhs_token) ⇒ void
This method returns an undefined value.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 123 def collect_parameters(path, document, rule, lhs_token) return if rule.parameters.empty? rule_id = lhs_token.span&.start_byte || 0 rule.parameters.zip(parameter_tokens(document, lhs_token)).each do |parameter, token| next unless token&.span add_occurrence( name: parameter, kind: :parameter, role: :definition, key: parameter_key(path, rule_id, parameter), path: path, span: token.span, data: { rule: rule.lhs } ) end end |
#collect_precedence(declaration) ⇒ void
This method returns an undefined value.
75 76 77 78 79 80 81 82 83 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 75 def collect_precedence(declaration) declaration.levels.each_with_index do |level, index| level.symbols.each do |name| @terminal_data[name][:precedence] = { direction: declaration.direction, associativity: level.associativity, level: index } end end end |
#collect_references(path, document) ⇒ void
This method returns an undefined value.
139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 139 def collect_references(path, document) ast = document.ast return unless ast ast.declarations.each_with_index do |declaration, index| following = ast.declarations[index + 1]&.loc || ast.rules.first&.loc collect_declaration_references(path, document, declaration, following) end ast.rules.each_with_index do |rule, index| collect_rule_references(path, document, rule, ast.rules[index + 1]&.loc) end end |
#collect_rule_definition(path, document, rule) ⇒ void
This method returns an undefined value.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 100 def collect_rule_definition(path, document, rule) token = token_at(document, rule.loc, rule.lhs) return unless token&.span @global_kinds[rule.lhs] ||= :rule signature = "#{rule.lhs}#{parameter_signature(rule.parameters)}" signature = "%inline #{signature}" if rule.inline data = { signature: signature, documentation: rule.documentation, inline: rule.inline, parameters: rule.parameters } @rule_data[rule.lhs] ||= data add_occurrence( name: rule.lhs, kind: :rule, role: :definition, key: global_key(rule.lhs), path: path, span: token.span, data: data ) collect_parameters(path, document, rule, token) end |
#collect_rule_references(path, document, rule, following_rule) ⇒ void
This method returns an undefined value.
168 169 170 171 172 173 174 175 176 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 168 def collect_rule_references(path, document, rule, following_rule) definition = token_at(document, rule.loc, rule.lhs) rule_id = definition&.span&.start_byte || 0 rule.alternatives.each_with_index do |alternative, index| alternative.items.each { |item| collect_item(path, document, item, rule, rule_id) } following = rule.alternatives[index + 1]&.loc || following_rule collect_alternative_precedence(path, document, alternative, following, rule, rule_id) end end |
#global_key(name) ⇒ Array[untyped]
243 244 245 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 243 def global_key(name) [:symbol, name] end |
#parameter_key(path, rule_id, name) ⇒ Array[untyped]
248 249 250 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 248 def parameter_key(path, rule_id, name) [:parameter, path, rule_id, name] end |
#parameter_signature(parameters) ⇒ String
253 254 255 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 253 def parameter_signature(parameters) parameters.empty? ? "" : "(#{parameters.join(', ')})" end |
#symbol_data(kind, name) ⇒ Hash[Symbol, untyped]
217 218 219 220 221 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 217 def symbol_data(kind, name) data = kind == :rule ? @rule_data[name] : @terminal_data[name] empty = {} #: Hash[Symbol, untyped] data || empty end |