Step 261: project-wide search and refactor (searchProject, renameSymbol)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bill
2026-02-11 20:27:47 +00:00
parent 8a461df47e
commit eab979bae3
7 changed files with 826 additions and 2 deletions

View File

@@ -17,6 +17,8 @@
#include "ast/Function.h"
#include "ast/Variable.h"
#include "ast/Import.h"
#include "ast/Expression.h"
#include "ast/Parameter.h"
#include "CompactAST.h" // getNodeName
namespace fs = std::filesystem;
@@ -265,6 +267,144 @@ inline std::vector<ExportedSymbol> collectExportedSymbols(
return symbols;
}
// -----------------------------------------------------------------------
// Step 261: SymbolReference — a single reference to a symbol
// -----------------------------------------------------------------------
struct SymbolReference {
std::string file; // source file path
int line = 0; // 1-based (from span if available)
int col = 0; // 1-based
std::string nodeId; // AST node ID
std::string kind; // "definition", "call", "reference", "parameter"
std::string context; // brief context (node type + name)
};
// Recursive helper: collect references to a name in a subtree
inline void collectSymbolRefsRecursive(
ASTNode* node, const std::string& name,
const std::string& filePath,
std::vector<SymbolReference>& refs) {
if (!node) return;
SymbolReference ref;
ref.file = filePath;
ref.nodeId = node->id;
ref.line = node->hasSpan() ? node->spanStartLine : 0;
ref.col = node->hasSpan() ? node->spanStartCol : 0;
bool matched = false;
if (node->conceptType == "Function") {
auto* fn = static_cast<const Function*>(node);
if (fn->name == name) {
ref.kind = "definition";
ref.context = "function " + fn->name;
matched = true;
}
} else if (node->conceptType == "FunctionCall") {
auto* fc = static_cast<const FunctionCall*>(node);
if (fc->functionName == name) {
ref.kind = "call";
ref.context = "call " + fc->functionName;
matched = true;
}
} else if (node->conceptType == "Variable") {
auto* v = static_cast<const Variable*>(node);
if (v->name == name) {
ref.kind = "definition";
ref.context = "variable " + v->name;
matched = true;
}
} else if (node->conceptType == "VariableReference") {
auto* vr = static_cast<const VariableReference*>(node);
if (vr->variableName == name) {
ref.kind = "reference";
ref.context = "ref " + vr->variableName;
matched = true;
}
} else if (node->conceptType == "Parameter") {
auto* p = static_cast<const Parameter*>(node);
if (p->name == name) {
ref.kind = "parameter";
ref.context = "param " + p->name;
matched = true;
}
}
if (matched) refs.push_back(std::move(ref));
for (auto* child : node->allChildren())
collectSymbolRefsRecursive(child, name, filePath, refs);
}
// Collect all references to a symbol name within a single AST
inline std::vector<SymbolReference> collectSymbolReferences(
Module* ast, const std::string& name,
const std::string& filePath) {
std::vector<SymbolReference> refs;
if (!ast || name.empty()) return refs;
collectSymbolRefsRecursive(ast, name, filePath, refs);
return refs;
}
// Build rename mutations for all references to a symbol in a single AST
struct RenameChange {
std::string file;
std::string nodeId;
std::string property; // which property to set
std::string oldValue;
std::string newValue;
std::string kind; // definition/call/reference/parameter
};
// Recursive helper: collect rename changes in a subtree
inline void buildRenameChangesRecursive(
ASTNode* node, const std::string& oldName,
const std::string& newName, const std::string& filePath,
std::vector<RenameChange>& changes) {
if (!node) return;
if (node->conceptType == "Function") {
auto* fn = static_cast<const Function*>(node);
if (fn->name == oldName)
changes.push_back({filePath, node->id, "name",
oldName, newName, "definition"});
} else if (node->conceptType == "FunctionCall") {
auto* fc = static_cast<const FunctionCall*>(node);
if (fc->functionName == oldName)
changes.push_back({filePath, node->id, "functionName",
oldName, newName, "call"});
} else if (node->conceptType == "Variable") {
auto* v = static_cast<const Variable*>(node);
if (v->name == oldName)
changes.push_back({filePath, node->id, "name",
oldName, newName, "definition"});
} else if (node->conceptType == "VariableReference") {
auto* vr = static_cast<const VariableReference*>(node);
if (vr->variableName == oldName)
changes.push_back({filePath, node->id, "variableName",
oldName, newName, "reference"});
} else if (node->conceptType == "Parameter") {
auto* p = static_cast<const Parameter*>(node);
if (p->name == oldName)
changes.push_back({filePath, node->id, "name",
oldName, newName, "parameter"});
}
for (auto* child : node->allChildren())
buildRenameChangesRecursive(child, oldName, newName,
filePath, changes);
}
inline std::vector<RenameChange> buildRenameChanges(
Module* ast, const std::string& oldName,
const std::string& newName, const std::string& filePath) {
std::vector<RenameChange> changes;
if (!ast || oldName.empty() || newName.empty()) return changes;
buildRenameChangesRecursive(ast, oldName, newName,
filePath, changes);
return changes;
}
// -----------------------------------------------------------------------
// ProjectState — aggregates workspace index and open buffer tracking
// -----------------------------------------------------------------------