// 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 }