Step 120: unify undo/redo snapshots

This commit is contained in:
Bill
2026-02-09 13:12:37 -07:00
parent 4f61953e1a
commit 5834523665
5 changed files with 164 additions and 35 deletions

View File

@@ -99,6 +99,9 @@ struct BufferState {
float splitScrollX = 0.0f;
float splitScrollY = 0.0f;
IncrementalOptimizer incrementalOptimizer;
Orchestrator orchestrator;
bool orchestratorDirty = true;
int undoDepth = 0;
};
struct EditorState {
@@ -165,8 +168,6 @@ struct EditorState {
std::vector<EditorDiagnostic> whetstoneDiagnostics;
SettingsManager settings;
bool showLspSettings = false;
Orchestrator orchestrator;
bool orchestratorDirty = true;
bool showSettingsPanel = false;
double lastAutoSave = 0.0;
ASTMutationAPI mutator;
@@ -343,7 +344,8 @@ struct EditorState {
state->bufferMode = mode;
activeBuffer = state.get();
bufferStates[path] = std::move(state);
orchestratorDirty = true;
active()->orchestratorDirty = true;
recordUndoSnapshot();
if (path.rfind("(untitled", 0) != 0) watcher.watch(path);
if (lsp && path.rfind("(untitled", 0) != 0) {
lsp->didOpen(toFileUri(path), language, content, activeBuffer->lspVersion);
@@ -430,7 +432,7 @@ struct EditorState {
if (!buffers.hasBuffer(path)) return;
buffers.switchToBuffer(path);
activeBuffer = bufferStates[path].get();
orchestratorDirty = true;
if (active()) active()->orchestratorDirty = true;
symbolsPending = true;
symbolsLastChange = ImGui::GetTime();
if (lsp) lsp->clearDocumentSymbols();
@@ -546,6 +548,7 @@ struct EditorState {
active()->highlightsDirty = true;
active()->generatedHighlightsDirty = true;
active()->modified = false;
recordUndoSnapshot();
}
} else {
doOpen(buf.path, bufferModeFromString(buf.mode));
@@ -555,7 +558,7 @@ struct EditorState {
if (!activePath.empty() && buffers.hasBuffer(activePath)) {
switchToBuffer(activePath);
}
orchestratorDirty = true;
if (active()) active()->orchestratorDirty = true;
return true;
} catch (...) {
return false;
@@ -681,28 +684,61 @@ struct EditorState {
if (!isStructured()) return;
Module* ast = active()->sync.getAST();
if (!ast) return;
orchestrator.setAST(cloneModule(ast));
orchestratorDirty = false;
active()->orchestrator.setAST(cloneModule(ast));
active()->orchestratorDirty = false;
}
void applyOrchestratorToActive() {
if (!active()) return;
if (!isStructured()) return;
Module* ast = orchestrator.getAST();
Module* ast = active()->orchestrator.getAST();
if (!ast) return;
active()->sync.setAST(cloneModule(ast));
active()->incrementalOptimizer.setRoot(active()->sync.getAST());
refreshActiveTextFromAST();
orchestratorDirty = false;
active()->orchestratorDirty = false;
recordUndoSnapshot();
}
Module* mutationAST() {
if (!active()) return nullptr;
if (!isStructured()) return nullptr;
if (orchestratorDirty || !orchestrator.getAST()) {
if (active()->orchestratorDirty || !active()->orchestrator.getAST()) {
syncOrchestratorFromActive();
}
return orchestrator.getAST();
return active()->orchestrator.getAST();
}
void recordUndoSnapshot() {
if (!active()) return;
Module* ast = isStructured() ? active()->sync.getAST() : nullptr;
active()->orchestrator.recordSnapshot(active()->editBuf, ast);
active()->undoDepth = active()->orchestrator.getUndoDepth();
}
void applySnapshotToActive(const std::string& text, std::unique_ptr<Module> ast) {
if (!active()) return;
active()->editBuf = text;
active()->editor.setContent(active()->editBuf, active()->language);
if (active()->bufferMode == BufferManager::BufferMode::Structured) {
if (ast) {
active()->orchestrator.setAST(cloneModule(ast.get()));
active()->sync.setAST(std::move(ast));
} else {
active()->sync.setText(active()->editBuf, active()->language);
active()->sync.syncNow();
active()->orchestrator.setAST(cloneModule(active()->sync.getAST()));
}
active()->incrementalOptimizer.setRoot(active()->sync.getAST());
}
active()->orchestratorDirty = false;
active()->highlightsDirty = true;
active()->generatedHighlightsDirty = true;
active()->modified = true;
if (lsp && active()->path.rfind("(untitled", 0) != 0) {
active()->lspVersion += 1;
lsp->didChange(toFileUri(active()->path), active()->editBuf, active()->lspVersion);
}
}
void registerCommand(const std::string& id,
@@ -845,7 +881,7 @@ struct EditorState {
active()->sync.syncNow();
active()->incrementalOptimizer.setRoot(active()->sync.getAST());
}
orchestratorDirty = true;
active()->orchestratorDirty = true;
active()->highlightsDirty = true;
}
@@ -858,7 +894,7 @@ struct EditorState {
active()->sync.syncNow();
active()->incrementalOptimizer.setRoot(active()->sync.getAST());
}
orchestratorDirty = true;
active()->orchestratorDirty = true;
active()->highlightsDirty = true;
active()->generatedHighlightsDirty = true;
active()->modified = true;
@@ -869,34 +905,27 @@ struct EditorState {
active()->lspVersion += 1;
lsp->didChange(toFileUri(active()->path), active()->editBuf, active()->lspVersion);
}
recordUndoSnapshot();
}
void doUndo() {
if (!active()) return;
active()->editor.undo();
active()->editBuf = active()->editor.getContent();
if (active()->bufferMode == BufferManager::BufferMode::Structured) {
active()->sync.setText(active()->editBuf, active()->language);
active()->sync.syncNow();
active()->incrementalOptimizer.setRoot(active()->sync.getAST());
std::string text;
std::unique_ptr<Module> ast;
if (active()->orchestrator.undoSnapshot(text, ast)) {
applySnapshotToActive(text, std::move(ast));
}
orchestratorDirty = true;
active()->highlightsDirty = true;
active()->generatedHighlightsDirty = true;
active()->undoDepth = active()->orchestrator.getUndoDepth();
}
void doRedo() {
if (!active()) return;
active()->editor.redo();
active()->editBuf = active()->editor.getContent();
if (active()->bufferMode == BufferManager::BufferMode::Structured) {
active()->sync.setText(active()->editBuf, active()->language);
active()->sync.syncNow();
active()->incrementalOptimizer.setRoot(active()->sync.getAST());
std::string text;
std::unique_ptr<Module> ast;
if (active()->orchestrator.redoSnapshot(text, ast)) {
applySnapshotToActive(text, std::move(ast));
}
orchestratorDirty = true;
active()->highlightsDirty = true;
active()->generatedHighlightsDirty = true;
active()->undoDepth = active()->orchestrator.getUndoDepth();
}
void doFind() {
@@ -2098,10 +2127,10 @@ int main(int, char**) {
}
if (ImGui::BeginMenu("Edit")) {
if (ImGui::MenuItem("Undo", state.keys.getBinding("edit.undo").toString().c_str(),
false, state.active() ? state.active()->editor.canUndo() : false))
false, state.active() ? state.active()->orchestrator.getUndoDepth() > 0 : false))
state.doUndo();
if (ImGui::MenuItem("Redo", state.keys.getBinding("edit.redo").toString().c_str(),
false, state.active() ? state.active()->editor.canRedo() : false))
false, state.active() ? state.active()->orchestrator.getRedoDepth() > 0 : false))
state.doRedo();
ImGui::Separator();
if (ImGui::MenuItem("Find/Replace", state.keys.getBinding("search.find").toString().c_str()))
@@ -4027,6 +4056,10 @@ int main(int, char**) {
ImGui::Text("Zoom: %d%%", zoomPercent(state.settings.getFontSize(), baseFontSize));
ImGui::SameLine(0, 30);
// Undo depth
ImGui::Text("Undo: %d", state.active() ? state.active()->undoDepth : 0);
ImGui::SameLine(0, 30);
// Modified indicator
if (state.active() && state.active()->modified)
ImGui::Text("Modified");