Skip to content

feat(tabs): a tab that shares a file name says which folder it is from - #728

Merged
PathGao merged 1 commit into
masterfrom
feat/duplicate-name-folder-suffix
Aug 29, 2026
Merged

feat(tabs): a tab that shares a file name says which folder it is from#728
PathGao merged 1 commit into
masterfrom
feat/duplicate-name-folder-suffix

Conversation

@PathGao

@PathGao PathGao commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

What this is

Two tabs open on .claude/skills/alpha/SKILL.md and .claude/skills/beta/SKILL.md both read SKILL.md, and the tab strip is the one surface where the path is not already on screen. Each tab now carries the containing folder after the file name, dimmed, and only while the name is actually ambiguous.

Reported by @SamHasler in #727, with VS Code named as the app that gets it right.

Closes #727

Mechanism

Nothing was wrong; there was no disambiguation at all. tab.title is the basename, assigned at the five places a tab acquires a path (openFile, claimPath, Save As, rename, session restore), and the tab strip renders it verbatim.

Two shapes were on the table:

  • VS Code (shorten() in vs/base/common/labels.ts) picks the shortest distinguishing segments from anywhere in the path and elides the rest with , giving …/skills/alpha. It reads as a path fragment.
  • JetBrains ("Show directory in editor tabs for non-unique filenames") appends the containing folder. It reads as a folder name, which is what a 200px tab has room for.

This is the second, with one correction. The containing folder alone is not always an answer: docs/api/README.md and packages/api/README.md would both read api, which is the original defect with a longer label. So a suffix that is still shared grows by one more folder — and only for the tabs that share it, so a sibling that was already unambiguous keeps its one-folder suffix rather than growing in sympathy.

Termination is not a loop bound: TabManager.claimPath makes a file path exclusive to one tab, so two paths cannot share every folder. The growable guard is there for the file at a filesystem root, which has no folder to give.

Whether a name is ambiguous is a fact about the set of open tabs, not about any one of them, so the suffixes are $derived in TabList and passed down as a prop. Writing them into tab.title would have been the shorter diff and the wrong one: that field also feeds the window title, the per-tab close dialog, nextUntitledTitle's numbering, and the session snapshot.

The setting is editor.showFolderForDuplicateNames, on by default — the suffix is silent until two tabs collide, so an existing install sees no change until the moment the strip stops answering the question it exists to answer.

Scope

Per window, not per app. VS Code disambiguates within one tab group and not across groups, and the analogue here is one window's strip. A suffix earned by a tab in another window would be noise: the reader cannot see that tab.

The setting row is gated on Show Tabs, class:inactive plus disabled, and sits directly below it. With the strip hidden the switch does nothing at all, which is the exact failure settingsSemantics.test.ts documents three prior instances of. Following that file's rule, the prerequisite is the row above.

Tab width is untouched. .tab is still max-width: 200px, so a suffix takes room from the file name. The existing hover marquee and the full-path title tooltip already cover an overflowing label, and widening tabs is a change to the strip's layout that this issue did not ask for. If README.md packages/api turns out to be too tight in practice, that is a one-line follow-up.

Case. Two files whose names differ only in case get no suffix. On Windows they cannot both be open (claimPath is exclusive on the path), and on a case-sensitive filesystem the names already read differently.

Tests

scripts/duplicateTabNames.test.ts, seven behaviour tests against the real module — one folder, growth on a shared suffix, the sibling that does not grow, a Windows path, the file at the root, and the non-files (untitled buffers, the home sentinel).

Revert the growth loop to a plain "containing folder, once" and keep the tests: three go red — the two that assert growth, plus the Windows one, whose fixture is a two-level collision. The other four stay green either way, which is right: they pin behaviour that a JetBrains-shaped implementation also has.

The render itself has no test. Mounting Tab.svelte in jsdom would earn "the prop is interpolated", which is weaker than what npm run check already gives, and AGENTS.md says not to migrate component tests for exactly that reason.

Verification

npm audit          found 0 vulnerabilities
npm run check      819 files, 0 errors, 0 warnings
npm test           995 pass, 0 fail
npm run test:vitest  401 pass, 0 fail  (45 files)
cargo test         164 pass, 0 fail

Ran on macOS 15 / arm64 as a release bundle, with five files laid out to hit each branch: skills/alpha|beta/SKILL.md for the one-folder case, and docs/api + packages/api + guide/README.md for growth — guide stays at one folder while the other two go to two. The suffix renders as intended and the settings row greys out with Show Tabs off.

Not verified: Windows and Linux. The separator choice (\ when the path is spelled with one) is reasoned from the path string, not observed — on Windows the folder suffix should read docs\api, and nothing in this branch has run there. The 26 locale strings past English and Simplified Chinese are unreviewed by a speaker.

Two tabs on .claude/skills/alpha/SKILL.md and .claude/skills/beta/SKILL.md
both read SKILL.md, and the tab strip is the one surface where the path is
not already on screen. Each now carries the containing folder, dimmed, and
only while the name is actually ambiguous.

The containing folder alone is not always an answer: docs/api/README.md and
packages/api/README.md would both read api. A suffix that is still shared
grows by one more folder, and only for the tabs that share it, so a sibling
that was already unambiguous keeps its one-folder suffix.

Whether a name is ambiguous is a fact about the set of open tabs, not about
any one of them, so the suffixes are derived in TabList rather than written
into Tab.title -- which also feeds the window title, the close dialog and
untitled numbering.

Behind Settings > Appearance > Show Folder for Duplicate Names, on by
default and inactive while Show Tabs is off, since the strip it annotates
is not on screen then.

ref #727
@PathGao
PathGao merged commit ae42826 into master Aug 29, 2026
4 checks passed
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.

Multiple tabs with same filename get folder slug appened

1 participant