lsp explain and index
This commit is contained in:
235
explain.cm
Normal file
235
explain.cm
Normal file
@@ -0,0 +1,235 @@
|
||||
// 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 && index.call_sites[_i].callee_symbol_id == found_sym.symbol_id) {
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user