Skip to content

Markdown preview: refresh on file-system change even when document is open#316756

Open
yogeshwaran-c wants to merge 1 commit into
microsoft:mainfrom
yogeshwaran-c:fix/markdown-preview-refresh-265277
Open

Markdown preview: refresh on file-system change even when document is open#316756
yogeshwaran-c wants to merge 1 commit into
microsoft:mainfrom
yogeshwaran-c:fix/markdown-preview-refresh-265277

Conversation

@yogeshwaran-c
Copy link
Copy Markdown
Contributor

Summary

Fixes #265277 — markdown preview keeps showing stale content after the file is overwritten on disk (external generator, file copy, editor agent, git pull, etc.).

Root cause

MarkdownPreview installs a FileSystemWatcher for the previewed resource but wraps the refresh in a guard:

if (!vscode.workspace.textDocuments.some(doc => areUrisEqual(doc.uri, uri))) {
    this.refresh();
}

The intent was to avoid a duplicate refresh when the file is also tracked as an open TextDocument, deferring to onDidChangeTextDocument in that case. In practice that assumption does not always hold for external overwrites: the text document model's reload from disk can be skipped or delayed by etag/stat caching, network/remote FS timing, or content-equality short-circuits, so onDidChangeTextDocument never fires for the change. The file-system event was the only remaining signal, and the guard silently dropped it — leaving the preview frozen on the pre-overwrite content until the user manually edited or invoked Markdown: Refresh preview.

This matches the persistent reports in the issue thread after #300629 landed (the case-insensitive URI fix only addressed the Windows-casing variant; users on Ubuntu/macOS/remote SSH continued to report it for non-casing reasons).

Fix

Drop the open-document guard so the watcher always calls refresh() when the previewed file changes on disk. The two paths can no longer drop each other's events.

Any genuine duplicate refresh that overlaps with onDidChangeTextDocument is already coalesced downstream:

  • MarkdownPreview.refresh() debounces via the 300 ms #throttleTimer, so two refresh() calls within the window collapse into one #updatePreview invocation.
  • #updatePreview early-returns when PreviewDocumentVersion matches the last rendered version, so a redundant invocation against an unchanged in-memory document is a no-op.

So unconditionally refreshing here does not introduce duplicate renders.

Test plan

Manual repro on Windows 11 (the original report's platform), confirmed before/after the change:

  • Create repro.md with some content, open it, then open the markdown preview side-by-side.
  • Externally overwrite repro.md with a different markdown file (copy a different file with the same name on top of it; or have an external script regenerate it).
  • Before the fix: preview keeps showing the old content; Markdown: Refresh preview is required.
  • After the fix: preview updates automatically on the file overwrite, without manual intervention.
  • Sanity-checked that ordinary in-editor edits still update the preview (the onDidChangeTextDocument path is unchanged).
  • No new lint or type errors (tsc -p extensions/markdown-language-features/tsconfig.json --noEmit clean).

A unit test isn't really feasible here without a real file-system + watcher harness — the bug is specifically about events that the in-process text model misses.

… open

When the previewed markdown file is overwritten on disk (e.g. a different
file copied over it, an external generator regenerates the file, an editor
agent rewrites it), the preview can keep showing stale content.

The file-system watcher in `MarkdownPreview` was guarded by:

  if (!vscode.workspace.textDocuments.some(doc => areUrisEqual(doc.uri, uri))) {
      this.refresh();
  }

The intent was to avoid a duplicate refresh when the file was also being
tracked as an open `TextDocument` (in which case `onDidChangeTextDocument`
would handle the update). In practice that assumption does not always
hold: the text document model's reload from disk can be skipped or
delayed by etag/stat caching, network/remote FS timing, or
content-equality short-circuits, so `onDidChangeTextDocument` never
fires for the change. The watcher event was the only remaining signal,
and the guard silently dropped it -- leaving the preview frozen on the
pre-overwrite content until the user manually edited or refreshed it.

Drop the guard so the watcher always calls `refresh()`. Any genuine
overlap with `onDidChangeTextDocument` is already coalesced by the
300ms `#throttleTimer` debounce and the `PreviewDocumentVersion`
equality check inside `#updatePreview`, so unconditionally refreshing
here does not introduce duplicate renders.

Fixes microsoft#265277
Copilot AI review requested due to automatic review settings May 16, 2026 05:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to make Markdown previews refresh when the backing file changes on disk, even if the document is already open in VS Code.

Changes:

  • Removes the open-document guard from the Markdown preview file-system watcher.
  • Adds explanatory comments about why file-system events should trigger refreshes unconditionally.

// content. Any genuine duplicate refresh that overlaps with `onDidChangeTextDocument`
// is already coalesced by the `#throttleTimer` debounce and version-equality check in
// `#updatePreview`, so unconditionally refreshing here is safe.
this.refresh();
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.

Markdown preview doesn't refresh/reload when the file is overwritten

3 participants