diff --git a/crates/ide-workspace/src/documents.rs b/crates/ide-workspace/src/documents.rs index 7bb741991..7ecd16a38 100644 --- a/crates/ide-workspace/src/documents.rs +++ b/crates/ide-workspace/src/documents.rs @@ -537,9 +537,15 @@ impl Documents { pub fn edit(&self, doc: DocId, base_version: u32, changes: ChangeSet) -> IdeResult { self.with_doc(doc, |st| { if st.read_only { + tracing::warn!("edit rejected: {} is read-only", st.path.display()); return Err(read_only_error(&st.path)); } - let (applied, edits) = st.doc.apply_detailed(base_version, &changes)?; + let (applied, edits) = st.doc.apply_detailed(base_version, &changes).inspect_err(|e| { + // A stale version is routine (the UI rebases); anything else makes the editor drop the edit. + if e.kind != ErrorKind::OutOfSync { + tracing::warn!("edit of {} rejected: {e}", st.path.display()); + } + })?; self.set_dirty(doc, st, true); self.notify_changed(doc, st, &applied, &edits); Ok(st.doc.version()) diff --git a/src/editor/core/model/document.ts b/src/editor/core/model/document.ts index b017024d9..95abb33b8 100644 --- a/src/editor/core/model/document.ts +++ b/src/editor/core/model/document.ts @@ -62,6 +62,7 @@ import { import { setMethodLines, softWrapActive } from "../cm/prefs"; import type { EditorSlot } from "./slot"; import { tx } from "@/platform/i18n"; +import { logError } from "@/platform/errorLog"; import { WorkspaceHistory } from "./workspaceHistory"; export type Banner = "binary" | "tooLarge" | "readOnly" | "library" | null; @@ -263,13 +264,15 @@ export class DocModel { }, { applyRemote: (cs, grouped) => this.applyRemote(cs, grouped), - onDiscarded: () => + onDiscarded: (reason) => { + logError(`document ${this.path}: unsaved edits replaced by the file's content`, reason); manager.notify({ level: "warning", title: "Editor resynchronised", body: tx`Some unsaved edits to ${this.path} could not be applied and were replaced by the file's current content.`, - }), - onError: (e) => console.warn(`document ${this.path}: sync error`, e), + }); + }, + onError: (e) => logError(`document ${this.path}: sync error`, e), }, manager.scheduler, ); diff --git a/src/editor/core/sync/DocSync.test.ts b/src/editor/core/sync/DocSync.test.ts index 2fa7933df..7b0e53c0f 100644 --- a/src/editor/core/sync/DocSync.test.ts +++ b/src/editor/core/sync/DocSync.test.ts @@ -226,7 +226,8 @@ describe("DocSync", () => { await server.processAll(); await flushMicrotasks(); expect(view.text.toString()).toBe("abc"); - expect(host.onDiscarded).toHaveBeenCalled(); + // The rejection reaches the host, which writes it to the log file. + expect(host.onDiscarded).toHaveBeenCalledWith(expect.objectContaining({ kind: "permissionDenied" })); expect(sync.idle).toBe(true); });