// cell explain — Query the semantic index for a source file. // // Usage: // cell explain --span file.ce:10:5 Find symbol at position // cell explain --symbol add_node Find symbol by name // cell explain --symbol add_node file.ce Limit to specific file // cell explain --help Show this help var fd = use('fd') var json = use('json') var tokenize_mod = use('tokenize') var parse_mod = use('parse') var fold_mod = use('fold') var index_mod = use('index') var explain_mod = use('explain') var shop = use('internal/shop') // Resolve import paths on an index in-place. var resolve_imports = function(idx_obj, fname) { var fi = shop.file_info(fd.realpath(fname)) var ctx = fi.package var ri = 0 var rp = null var lp = null while (ri < length(idx_obj.imports)) { rp = shop.resolve_use_path(idx_obj.imports[ri].module_path, ctx) // Fallback: check sibling files in the same directory. if (rp == null) { lp = fd.dirname(fd.realpath(fname)) + '/' + idx_obj.imports[ri].module_path + '.cm' if (fd.is_file(lp)) { rp = lp } } if (rp != null) { idx_obj.imports[ri].resolved_path = rp } ri = ri + 1 } } var mode = null var span_arg = null var symbol_name = null var files = [] var i = 0 var parts = null var filename = null var line = null var col = null var src = null var idx = null var indexes = [] var explain = null var result = null var pipeline = {tokenize: tokenize_mod, parse: parse_mod, fold: fold_mod} for (i = 0; i < length(args); i++) { if (args[i] == '--span') { mode = "span" if (i + 1 < length(args)) { span_arg = args[i + 1] i = i + 1 } else { log.error('--span requires file:line:col') $stop() } } else if (args[i] == '--symbol') { mode = "symbol" if (i + 1 < length(args)) { symbol_name = args[i + 1] i = i + 1 } else { log.error('--symbol requires a name') $stop() } } else if (args[i] == '--help' || args[i] == '-h') { log.console("Usage: cell explain [options]") log.console("") log.console("Query the semantic index for a source file.") log.console("") log.console("Options:") log.console(" --span file:line:col Find symbol at position") log.console(" --symbol name ... Find symbol by name across files") $stop() } else if (!starts_with(args[i], '-')) { files[] = args[i] } } if (mode == null) { log.error('Specify --span or --symbol. Use --help for usage.') $stop() } if (mode == "span") { parts = array(span_arg, ":") if (length(parts) < 3) { log.error('--span requires file:line:col format') $stop() } filename = parts[0] line = number(parts[1]) col = number(parts[2]) if (!fd.is_file(filename)) { log.error('File not found: ' + filename) $stop() } src = text(fd.slurp(filename)) idx = index_mod.index_file(src, filename, pipeline) resolve_imports(idx, filename) explain = explain_mod.make(idx) result = explain.at_span(line, col) if (result == null) { log.console("Nothing found at " + filename + ":" + text(line) + ":" + text(col)) } else { print(json.encode(result, true)) print("\n") } } if (mode == "symbol") { if (length(files) == 0) { log.error('--symbol requires at least one file argument') $stop() } // Validate all files exist. i = 0 while (i < length(files)) { if (!fd.is_file(files[i])) { log.error('File not found: ' + files[i]) $stop() } i = i + 1 } if (length(files) == 1) { // Single file: use by_symbol for a focused result. filename = files[0] src = text(fd.slurp(filename)) idx = index_mod.index_file(src, filename, pipeline) resolve_imports(idx, filename) explain = explain_mod.make(idx) result = explain.by_symbol(symbol_name) if (result == null || length(result.symbols) == 0) { log.console("Symbol '" + symbol_name + "' not found in " + filename) } else { print(json.encode(result, true)) print("\n") } } else if (length(files) > 1) { // Multiple files: index each and search across all. indexes = [] i = 0 while (i < length(files)) { src = text(fd.slurp(files[i])) idx = index_mod.index_file(src, files[i], pipeline) resolve_imports(idx, files[i]) indexes[] = idx i = i + 1 } result = explain_mod.explain_across(indexes, symbol_name) if (result == null || length(result.symbols) == 0) { log.console("Symbol '" + symbol_name + "' not found in " + text(length(files)) + " files") } else { print(json.encode(result, true)) print("\n") } } } $stop()