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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user