Skip to content

Add new behaviors and appearance around deleted files - #1588

Merged
savetheclocktower merged 20 commits into
masterfrom
deleted-file-behavior
Oct 17, 2026
Merged

savetheclocktower merged 20 commits into
masterfrom
deleted-file-behavior

Conversation

@savetheclocktower

Copy link
Copy Markdown
Contributor

Here’s everything this PR does:

  • Bumps @pulsar-edit/text-buffer to 15.0.0.

    • This adds TextBuffer::isDeleted (returns true if the buffer is not backed by a file on disk, but used to be).
    • It also changes the behavior of isModified (which previously always returned true for buffers without a file on disk) to be more nuanced around files with “deleted” status:
      • If the file was not in a modified state when it was deleted, isModified will return false until and unless the user makes further buffer changes.
      • If the file was in a modified state when it was deleted, isModified will return true as it did before.
  • The above bump implicitly changes Pulsar’s behavior when the user tries to close a buffer with “deleted” status. Because isModified returns false when the buffer was not in a modified state at time of file deletion, such files will be able to be closed by the user without an “are you sure?” prompt. This matches VS Code’s behavior.

    • If the buffer was dirty at time of deletion, then the user has made changes that could be lost. We still show the “are you sure?” prompt in these scenarios, but with a more accurate message in the dialog.
    • These behavior changes are optional; they are governed by the new editor.promptWhenAbandoningDeletedFile setting. Because others have concerns about this change in behavior, this setting will (at least for now) default to true — which means this new behavior is opt-in.
  • Adds new behavior to the tabs package so that it can detect these new states (including the “conflicted” state introduced in Guard against a user saving while the text buffer is conflicted… #1478) and optionally add class names to tabs accordingly so that UI themes can style them.

    • This new behavior is governed by the new tabs.enableItemStatusColoring setting, which defaults to true.
  • Updates the built-in UI themes to style tabs appropriately for each theme when a tab is in the “deleted” state. This mimics how VS Code represents deleted files in the tab — in red, with a strike-through effect.

    • No equivalent styles have been introduced for the “conflicted” state — though that class name will be added to tabs if tabs.enableItemStatusColoring is true, so a community theme could style conflicted pane items however it wants.
  • Does some other refactors in pane.js, including some more precise API documentation.

  • Tests were written in text-buffer and are also written for the tabs package. I should write at least one more for the editor.promptWhenAbandoningDeletedFile setting, so I’ll keep this PR in draft until that happens.

@savetheclocktower savetheclocktower added this to the 1.133.0 milestone May 31, 2026
@savetheclocktower savetheclocktower linked an issue May 31, 2026 that may be closed by this pull request
@savetheclocktower

Copy link
Copy Markdown
Contributor Author

The find-and-replace failure is just flakiness, but the autosave failure is real. I opened pulsar-edit/text-buffer#15 to fix it. Once that lands, I'll put out text-buffer@15.0.1 and update this PR; once CI is green I'll take this out of draft.

@savetheclocktower

Copy link
Copy Markdown
Contributor Author

Nope, taking out of draft will wait at least a bit longer. Discovered another issue (pulsar-edit/text-buffer#16) and opened another PR (pulsar-edit/text-buffer#17).

Don't worry. If this isn't ready for prime time when we're ready to ship 1.133.0, it'll slip to 1.134.0.

@savetheclocktower

savetheclocktower commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor Author

As a reminder to myself: once pulsar-edit/text-buffer#15 and pulsar-edit/text-buffer#17 both land, I'll do a text-buffer version bump and release. This PR will need to bump text-buffer and then wire up the “resurrection detection” described in pulsar-edit/text-buffer#17 — a task for our project-wide recursive file-event watcher.

When there is no project root in the window, we can't rely on a watcher to be present for resurrection detection… so instead we might want to have a fallback behavior. For instance, if we think a file has been deleted, we can re-check that assumption on demand whenever the associated editor is focused.

…by bumping to new versions of `superstring` and `text-buffer`.

Resurrection will happen via file-watcher (for project files) and when the window is foregrounded (for the active editor, if any) or when a given editor is made active. These latter strategies function as fallbacks for buffers that are from outside project roots.
@savetheclocktower
savetheclocktower marked this pull request as ready for review October 17, 2026 00:17
…rather than `onDidDelete`/`onDidConflict`. (If the newer methods are not available on a pane item, we'll fall back to the older methods.)
…to indicate a tab's “conflicted” or “deleted” status.
@savetheclocktower

savetheclocktower commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor Author

OK, it's finally working exactly like it ought to. Here's a guide:

Screenshot 2026-10-16 at 5 45 12 PM

This is our sample file. In this screenshot it exists on disk and is unmodified in the buffer.

Deletion/resurrection while unmodified

Suppose I delete the file from the terminal while the buffer is open. If core.closeDeletedFileTabs is enabled, the tab will disappear. Otherwise, it'll transition into the new “deleted” state:

Screenshot 2026-10-16 at 5 45 25 PM

The treatment in the tab is customizable by themes; it's just a new deleted class name in addition to class names like modified and pending that are already used.

What happens when I close the tab? Depends on the value of the new editor.promptWhenAbandoningDeletedFile setting. When that setting is enabled (as it is by default, at least for now), there is no change from the previous behavior. But if you opt into this new paradigm by unchecking that config setting, text editors will no longer prompt to save simply because the backing file has been deleted. That means that you'll be able to close such a text editor without a prompt!

This happens because we no longer automatically consider an editor to be in the “modified” state when the backing file is deleted. If the buffer was unmodified at time of deletion and has not been modified after deletion, then it never gains a “modified” status. (Despite this, TextEditor#shouldPromptToSave will still prompt if editor.promptWhenAbandoningDeletedFile is true. That setting doesn't change the definition of “modified”; it just becomes part of the calculus of deciding whether to prompt upon closing an editor.)

But suppose I don't close the tab. Suppose I restore the original file — meaning I take the exact contents that were there previously and put them back at test.txt:

Screenshot 2026-10-16 at 5 45 12 PM

I'm back where I was. The file exists again, so the “deleted” state is removed; and its contents match what we already had in the buffer, so no “modified” or “conflicted” state is added.

Deletion/resurrection while modified

In this scenario, I open the file and make some changes, but do not commit them:

Screenshot 2026-10-16 at 5 46 06 PM

The editor is in the “modified” state, as we expect. If I delete test.txt, I find that the editor gained “deleted” status and retained its “modified” status:

Screenshot 2026-10-16 at 5 46 17 PM

Hence if I tried to close the tab now, I'd get prompted (since the buffer is still considered modified).

The exact same thing will happen if I wait until file deletion and then modify the buffer.

Deletion/restoration with new content

All of the above behavior works as described because we remember what the old text was. (superstring does this for free, since it tracks uncommitted buffer edits in terms of diffs applied to the original text.)

So that means if I delete test.txt, but restore it with different contents, the buffer will do one of two things:

Unmodified

If the buffer was not modified at the time that the new file contents were written to test.txt, then the buffer successfully changes to reflect the new content. Easy.

Modified

But if we had uncommitted changes at the time of restoration, this is the textbook definition of the “conflicted“ state, albeit with extra steps. Just like if we had overwritten the file with different content, the outcome is the same even though we deleted the file first before writing the new content to test.txt. Hence the buffer will transition from “deleted” to “conflicted”:

Screenshot 2026-10-16 at 5 47 01 PM

At that point, conflicted semantics and behaviors apply. There's another experimental setting (introduced in 1.132) called core.promptOnConflict; if it's enabled, then you get prompted when you try to save the modified buffer to disk:

Screenshot 2026-10-16 at 6 05 00 PM

@savetheclocktower

Copy link
Copy Markdown
Contributor Author

OK, CI was green until the last commit — which only tweaks some CSS styles. Landing now!

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

Labels

None yet

Projects

None yet

1 participant