239 lines
5.6 KiB
Plaintext
239 lines
5.6 KiB
Plaintext
// explain.cm — Query module over a semantic index.
|
|
//
|
|
// Usage:
|
|
// var explain = use('explain').make(index)
|
|
// explain.at_span(line, col)
|
|
// explain.by_symbol(name)
|
|
// explain.call_chain(symbol_id, depth)
|
|
|
|
// Check if a position (line, col) falls inside a span.
|
|
var span_contains = function(span, line, col) {
|
|
if (line < span.from_row || line > span.to_row) return false
|
|
if (line == span.from_row && col < span.from_col) return false
|
|
if (line == span.to_row && col > span.to_col) return false
|
|
return true
|
|
}
|
|
|
|
// Create an explain interface bound to a single file index.
|
|
var make = function(index) {
|
|
|
|
// Find symbol or reference at a given line/col position.
|
|
var at_span = function(line, col) {
|
|
var _i = 0
|
|
var sym = null
|
|
var ref = null
|
|
var found_sym = null
|
|
var found_ref = null
|
|
var result_refs = []
|
|
var result_calls = []
|
|
|
|
// Search symbols for one whose decl_span contains (line, col).
|
|
_i = 0
|
|
while (_i < length(index.symbols)) {
|
|
sym = index.symbols[_i]
|
|
if (sym.decl_span != null && span_contains(sym.decl_span, line, col)) {
|
|
found_sym = sym
|
|
break
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
|
|
// If no symbol found, search references.
|
|
if (found_sym == null) {
|
|
_i = 0
|
|
while (_i < length(index.references)) {
|
|
ref = index.references[_i]
|
|
if (ref.span != null && span_contains(ref.span, line, col)) {
|
|
found_ref = ref
|
|
// Look up the symbol this reference points to.
|
|
if (ref.symbol_id != null) {
|
|
_i = 0
|
|
while (_i < length(index.symbols)) {
|
|
if (index.symbols[_i].symbol_id == ref.symbol_id) {
|
|
found_sym = index.symbols[_i]
|
|
break
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
}
|
|
break
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
}
|
|
|
|
if (found_sym == null && found_ref == null) return null
|
|
|
|
// Gather all references to this symbol.
|
|
if (found_sym != null && index.reverse_refs[found_sym.name] != null) {
|
|
result_refs = index.reverse_refs[found_sym.name]
|
|
}
|
|
|
|
// Gather call sites.
|
|
_i = 0
|
|
while (_i < length(index.call_sites)) {
|
|
if (found_sym != null) {
|
|
if (index.call_sites[_i].callee_symbol_id == found_sym.symbol_id ||
|
|
(index.call_sites[_i].callee_symbol_id == null && index.call_sites[_i].callee == found_sym.name)) {
|
|
result_calls[] = index.call_sites[_i]
|
|
}
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
|
|
return {
|
|
symbol: found_sym,
|
|
reference: found_ref,
|
|
references: result_refs,
|
|
call_sites: result_calls,
|
|
imports: index.imports
|
|
}
|
|
}
|
|
|
|
// Find all symbols matching a name.
|
|
var by_symbol = function(name) {
|
|
var _i = 0
|
|
var matches = []
|
|
var result_refs = []
|
|
var result_calls = []
|
|
|
|
// Find matching symbols.
|
|
_i = 0
|
|
while (_i < length(index.symbols)) {
|
|
if (index.symbols[_i].name == name) {
|
|
matches[] = index.symbols[_i]
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
|
|
// Gather all references to this name.
|
|
if (index.reverse_refs[name] != null) {
|
|
result_refs = index.reverse_refs[name]
|
|
}
|
|
|
|
// Gather call sites where this name is the callee.
|
|
_i = 0
|
|
while (_i < length(index.call_sites)) {
|
|
if (index.call_sites[_i].callee == name) {
|
|
result_calls[] = index.call_sites[_i]
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
|
|
return {
|
|
symbols: matches,
|
|
references: result_refs,
|
|
call_sites: result_calls
|
|
}
|
|
}
|
|
|
|
// Build a call chain from/to a symbol.
|
|
var call_chain = function(symbol_id, depth) {
|
|
var max_depth = (depth != null) ? depth : 2
|
|
var callers = []
|
|
var callees = []
|
|
var _i = 0
|
|
var cs = null
|
|
|
|
// Callees: calls made FROM this symbol.
|
|
_i = 0
|
|
while (_i < length(index.call_sites)) {
|
|
cs = index.call_sites[_i]
|
|
if (cs.enclosing == symbol_id) {
|
|
callees[] = {
|
|
callee: cs.callee,
|
|
callee_symbol_id: cs.callee_symbol_id,
|
|
span: cs.span,
|
|
args_count: cs.args_count
|
|
}
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
|
|
// Callers: calls TO this symbol.
|
|
_i = 0
|
|
while (_i < length(index.call_sites)) {
|
|
cs = index.call_sites[_i]
|
|
if (cs.callee_symbol_id == symbol_id) {
|
|
callers[] = {
|
|
from: cs.enclosing,
|
|
span: cs.span,
|
|
args_count: cs.args_count
|
|
}
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
|
|
return {
|
|
symbol_id: symbol_id,
|
|
callers: callers,
|
|
callees: callees,
|
|
depth: max_depth
|
|
}
|
|
}
|
|
|
|
return {
|
|
at_span: at_span,
|
|
by_symbol: by_symbol,
|
|
call_chain: call_chain
|
|
}
|
|
}
|
|
|
|
// Search across multiple file indexes.
|
|
var explain_across = function(indexes, name) {
|
|
var _i = 0
|
|
var _j = 0
|
|
var all_symbols = []
|
|
var all_refs = []
|
|
var all_calls = []
|
|
var idx = null
|
|
var refs = null
|
|
|
|
_i = 0
|
|
while (_i < length(indexes)) {
|
|
idx = indexes[_i]
|
|
|
|
// Gather symbols.
|
|
_j = 0
|
|
while (_j < length(idx.symbols)) {
|
|
if (idx.symbols[_j].name == name) {
|
|
all_symbols[] = idx.symbols[_j]
|
|
}
|
|
_j = _j + 1
|
|
}
|
|
|
|
// Gather references.
|
|
refs = idx.reverse_refs[name]
|
|
if (refs != null) {
|
|
_j = 0
|
|
while (_j < length(refs)) {
|
|
all_refs[] = refs[_j]
|
|
_j = _j + 1
|
|
}
|
|
}
|
|
|
|
// Gather call sites.
|
|
_j = 0
|
|
while (_j < length(idx.call_sites)) {
|
|
if (idx.call_sites[_j].callee == name) {
|
|
all_calls[] = idx.call_sites[_j]
|
|
}
|
|
_j = _j + 1
|
|
}
|
|
|
|
_i = _i + 1
|
|
}
|
|
|
|
return {
|
|
symbols: all_symbols,
|
|
references: all_refs,
|
|
call_sites: all_calls
|
|
}
|
|
}
|
|
|
|
return {
|
|
make: make,
|
|
explain_across: explain_across,
|
|
span_contains: span_contains
|
|
}
|