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(name:, kind:, role:, key:, path:, span:, data:) ⇒ 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) ⇒ symbol_key
-
#initialize(store, files) ⇒ SymbolIndexBuilder
constructor
A new instance of SymbolIndexBuilder.
- #parameter_key(path, rule_id, name) ⇒ symbol_key
- #parameter_signature(parameters) ⇒ String
- #symbol_data(kind, name) ⇒ symbol_data
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, symbol_data] @terminal_data = Hash.new { |hash, key| hash[key] = {} } #: Hash[String, symbol_data] end |
Instance Method Details
#add_global_reference(path, token, name) ⇒ void
This method returns an undefined value.
212 213 214 215 216 217 218 219 220 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 212 def add_global_reference(path, token, name) span = token.span return unless 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: span, data: data) end |
#add_named_tokens(path, tokens, names, kind, role) ⇒ void
This method returns an undefined value.
230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 230 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) span = token&.span next unless span @global_kinds[name] ||= kind add_occurrence(name: name, kind: kind, role: role, key: global_key(name), path: path, span: span, data: @terminal_data[name]) end end |
#add_occurrence(name:, kind:, role:, key:, path:, span:, data:) ⇒ void
This method returns an undefined value.
246 247 248 249 250 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 246 def add_occurrence(name:, kind:, role:, key:, path:, span:, data:) @occurrences << SymbolOccurrence.new( name: name, kind: kind, role: role, key: key, path: path, span: span, data: data ) 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?
268 269 270 271 272 273 274 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 268 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.
159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 159 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 98 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 86 def collect_include(path, tokens, declaration) token = tokens.find { |candidate| candidate.type == :literal } span = token&.span return unless span target = canonical_include_target(declaration) return unless target add_occurrence( name: declaration.path, kind: :include, role: :reference, key: [:include, target], path: path, span: span, data: { target: target } ) end |
#collect_item(path, document, item, rule, rule_id) ⇒ void
This method returns an undefined value.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 185 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.
204 205 206 207 208 209 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 204 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.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 127 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| span = token&.span next unless span add_occurrence( name: parameter, kind: :parameter, role: :definition, key: parameter_key(path, rule_id, parameter), path: path, span: 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.
144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 144 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.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 101 def collect_rule_definition(path, document, rule) token = token_at(document, rule.loc, rule.lhs) span = token&.span return unless span lhs_token = token #: Frontend::Token @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: span, data: data ) collect_parameters(path, document, rule, lhs_token) end |
#collect_rule_references(path, document, rule, following_rule) ⇒ void
This method returns an undefined value.
173 174 175 176 177 178 179 180 181 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 173 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) ⇒ symbol_key
253 254 255 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 253 def global_key(name) [:symbol, name] end |
#parameter_key(path, rule_id, name) ⇒ symbol_key
258 259 260 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 258 def parameter_key(path, rule_id, name) [:parameter, path, rule_id, name] end |
#parameter_signature(parameters) ⇒ String
263 264 265 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 263 def parameter_signature(parameters) parameters.empty? ? "" : "(#{parameters.join(', ')})" end |
#symbol_data(kind, name) ⇒ symbol_data
223 224 225 226 227 |
# File 'lib/ibex/lsp/symbol_index_builder.rb', line 223 def symbol_data(kind, name) data = kind == :rule ? @rule_data[name] : @terminal_data[name] empty = {} #: symbol_data data || empty end |