Steps 263-265: Phase 9e — save buffer to disk, undo/redo, integration tests

Step 263: saveBuffer/saveAllBuffers RPC methods write editBuf to disk with
workspace path resolution and parent dir creation. 12/12 tests.

Step 264: State-based undo/redo via HeadlessUndoStack. Snapshots recorded
automatically before mutations; undo/redo restore AST + editBuf. 12/12 tests.

Step 265: Phase 9e integration tests — save after undo/redo, mixed undo
states across buffers, full open→mutate→diagnose→undo→redo→save. 8/8 tests.

Phase 9e complete: 32/32 tests. Sprint 9 total: 244/244 across 5 phases.
34 MCP tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bill
2026-02-12 01:27:23 +00:00
parent 872ef493d2
commit c36fd92045
9 changed files with 1536 additions and 4 deletions

View File

@@ -61,6 +61,44 @@ struct HeadlessLibraryState {
SemanticTags semanticTags;
};
// -----------------------------------------------------------------------
// HeadlessUndoStack — state-based undo/redo for headless buffers
// -----------------------------------------------------------------------
struct HeadlessUndoState {
std::string text;
json astJson;
};
class HeadlessUndoStack {
public:
// Record current state as a checkpoint
void record(const std::string& text, Module* ast) {
// Truncate any redo states
if (position_ + 1 < (int)states_.size())
states_.resize(position_ + 1);
HeadlessUndoState s;
s.text = text;
if (ast) s.astJson = toJson(ast);
states_.push_back(std::move(s));
position_ = (int)states_.size() - 1;
}
bool canUndo() const { return position_ > 0; }
bool canRedo() const { return position_ + 1 < (int)states_.size(); }
const HeadlessUndoState& undo() { return states_[--position_]; }
const HeadlessUndoState& redo() { return states_[++position_]; }
int undoDepth() const { return position_; }
int redoDepth() const {
return (int)states_.size() - position_ - 1;
}
private:
std::vector<HeadlessUndoState> states_;
int position_ = -1;
};
// -----------------------------------------------------------------------
// HeadlessBufferState — lightweight buffer without ImGui widgets
// -----------------------------------------------------------------------
@@ -70,6 +108,7 @@ struct HeadlessBufferState {
IncrementalOptimizer incrementalOptimizer;
ASTVersionTracker versionTracker;
DiagnosticVersionTracker diagTracker;
HeadlessUndoStack undoStack;
std::string language = "python";
std::string path = "(untitled)";
std::string editBuf;
@@ -205,6 +244,8 @@ struct HeadlessEditorState {
buf->orchestratorDirty = true;
}
HeadlessBufferState* raw = buf.get();
// Record initial state for undo
raw->undoStack.record(raw->editBuf, raw->sync.getAST());
bufferStates[filePath] = std::move(buf);
if (!activeBuffer) activeBuffer = raw;
return raw;