Skip to content

fix(editor): use versioned dirty state and debounced cache writes, also avoid full string conversion of editor buffer #2145

Open
bajrangCoder wants to merge 6 commits into
Acode-Foundation:mainfrom
bajrangCoder:editor-versioned-dirty-cache
Open

fix(editor): use versioned dirty state and debounced cache writes, also avoid full string conversion of editor buffer #2145
bajrangCoder wants to merge 6 commits into
Acode-Foundation:mainfrom
bajrangCoder:editor-versioned-dirty-cache

Conversation

@bajrangCoder
Copy link
Copy Markdown
Member

  • track editor dirty state with version metadata and CodeMirror Text equality
  • debounce cache snapshots and flush pending writes on switch/pause/save
  • avoid full doc string conversion for tab switches and equality checks
  • use mtime metadata for external file change detection

- track editor dirty state with version metadata and CodeMirror Text equality
- debounce cache snapshots and flush pending writes on switch/pause/save
- avoid full doc string conversion for tab switches and equality checks
- use mtime metadata for external file change detection
@bajrangCoder bajrangCoder added the CI: RUN ON-DEMAND PREVIEW RELEASES Triggers an on-demand preview build for this pull request via CI workflow. label Jun 2, 2026
@github-actions github-actions Bot removed the CI: RUN ON-DEMAND PREVIEW RELEASES Triggers an on-demand preview build for this pull request via CI workflow. label Jun 2, 2026
@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

Preview Release for this, has been built.

Click here to view that github actions build

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 2, 2026

Greptile Summary

This PR refactors the editor's dirty-state and crash-cache pipeline: it replaces the async text-comparison approach with version counters (docVersion/savedVersion/cacheVersion) and CodeMirror Text.eq equality checks, debounces cache writes with a flush on tab-switch and app-pause, and introduces mtime metadata to short-circuit redundant checkFiles full-reads.

  • Version metadata (markLoaded, markSaved, markEdited, markDiskChanged) tracks document state across the load → edit → save → reload lifecycle alongside scheduleCacheWrite/flushCacheWrite.
  • checkFiles now uses savedMtime to skip stat-equal files and warns on disk conflicts for unsaved files, but the scheduleCacheWrite(0) after a reload is a no-op because markLoaded resets all three version counters to 0 first.
  • saveFile correctly snapshots savedDoc/savedVersion before the write, but the formatOnSave block lacks try/finally, leaving markChanged permanently false if format throws with no fallback restore path in the new listener design.

Confidence Score: 3/5

Not safe to merge as-is: two correctness issues in the dirty-state code paths can silently prevent edit tracking or misreport conflict state.

The formatOnSave path in saveFile.js can permanently silence markEdited() if the format executor throws, because the new dispatch listener returns early when markChanged === false and never sets up the checkTimeout that previously served as a fallback restore. The isUnsaved setter also silently advances savedVersion on every undo-to-clean transition, which can suppress a hasDiskConflict alert when the disk file is externally modified.

src/lib/saveFile.js (formatOnSave markChanged guard) and src/lib/editorFile.js (isUnsaved setter side-effect on savedVersion)

Important Files Changed

Filename Overview
src/lib/editorFile.js Core dirty-state engine: adds versioned tracking (docVersion/savedVersion/cacheVersion), #savedDoc reference, debounced cache writes, and markLoaded/markSaved/markEdited lifecycle methods.
src/lib/saveFile.js Adds pre-write snapshot of savedDoc/savedVersion and post-write stat for mtime; the markChanged restoration after formatOnSave lacks try/finally protection.
src/lib/checkFiles.js Adds mtime short-circuit and disk-conflict detection; scheduleCacheWrite(0) after reload is a no-op (markLoaded resets all versions to 0).
src/lib/editorManager.js Dispatch listener calls markEdited() synchronously; adds flushCacheWrites() and per-tab-switch flushCacheWrite.
src/lib/saveState.js Persists new version metadata fields for crash-recovery.
src/lib/openFile.js Passes savedMtime/diskMtime from fileInfo stat; switches equality check to CodeMirror Text.eq.
src/main.js pauseHandler now awaits flushCacheWrites() before save-state.
src/utils/helpers.js Adds normalizeMtime and getStatMtime utility helpers.

Sequence Diagram

sequenceDiagram
    participant User
    participant CM as CodeMirror Dispatch
    participant Listener as updateListener
    participant File as EditorFile
    participant Cache as CacheFS

    User->>CM: keystroke
    CM->>Listener: "update (docChanged=true)"
    Listener->>File: "file.session = update.state"
    alt "markChanged === false"
        Listener-->>Listener: return early (no markEdited, no timeout)
    else normal edit
        Listener->>File: markEdited() docVersion++
        Listener->>Listener: set checkTimeout
        Listener-->>Listener: checkTimeout fires
        Listener->>File: scheduleCacheWrite(1500ms)
        File->>Cache: writeToCache()
    end
    Note over File,Cache: Tab switch or app pause
    File->>File: flushCacheWrite()
    File->>Cache: writeToCache() immediately
    Note over File,Cache: Save
    User->>File: save()
    File->>File: snapshot savedDoc, savedVersion
    File->>Cache: writeFile(data)
    File->>Cache: stat() mtime
    File->>File: markSaved()
    Note over File,Cache: checkFiles
    File->>Cache: stat() mtime
    alt "mtime === savedMtime"
        Cache-->>File: return early
    else mtime differs and isUnsaved
        File->>File: markDiskChanged conflict warning
    else mtime differs and not isUnsaved
        Cache->>File: readFile Text.eq comparison
        alt content differs
            File->>File: markLoaded scheduleCacheWrite(0) no-op
        else content same
            File->>File: markLoaded update savedMtime
        end
    end
Loading

Reviews (5): Last reviewed commit: "fix(editor): refresh cache after disk re..." | Re-trigger Greptile

Comment thread src/lib/editorFile.js
Comment thread src/lib/editorFile.js
@bajrangCoder

This comment was marked as outdated.

Comment thread src/lib/saveFile.js Outdated
@bajrangCoder

This comment was marked as outdated.

@bajrangCoder

This comment was marked as outdated.

Comment thread src/lib/checkFiles.js
@bajrangCoder
Copy link
Copy Markdown
Member Author

@greptileai review again and update scores etc

Comment thread src/lib/saveFile.js
Comment on lines 119 to 123
if (appSettings.value.formatOnSave) {
editorManager.activeFile.markChanged = false;
acode.exec("format", false);
editorManager.activeFile.markChanged = true;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 The markChanged = true restoration is not inside a try/finally block. In the new dispatch-listener design, the checkTimeout callback (which used to serve as a backup restore for markChanged) no longer fires when markChanged === false, because the listener returns early before setting up the timeout. If acode.exec("format", false) throws or dispatches asynchronously, markChanged can be left permanently false for this file. Every subsequent user edit will be ignored by the listener — markEdited() never runs, docVersion never increments, and the unsaved indicator stays dark despite real edits.

Suggested change
if (appSettings.value.formatOnSave) {
editorManager.activeFile.markChanged = false;
acode.exec("format", false);
editorManager.activeFile.markChanged = true;
}
if (appSettings.value.formatOnSave) {
editorManager.activeFile.markChanged = false;
try {
acode.exec("format", false);
} finally {
editorManager.activeFile.markChanged = true;
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant