Files
cell/explain.cm

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
}