Compare TabsAdds file comparison to the editor tab context menu, plus a Paste Compare panel for diffing two pasted texts — with the formatting and normalisation that makes such a diff readable in the first place. Features1. Compare via tab context menu
("Compare with Selected" only appears after a tab has been selected, matching the built-in Explorer behavior. If the selected file has since been deleted, you are told rather than shown a broken diff.) 2. Paste CompareClick The diff is backed by an in-memory file system ( Language is auto-detected from the pasted text and applied to both sides for syntax highlighting (JSON, JSONC, YAML, XML, HTML, PHP, SQL, Python, Shell, Dockerfile, diff, C/C++, Java, Go, Rust, TypeScript, JavaScript, CSS, Markdown, INI/TOML, .properties). Click the language in the status bar to override it. Line counts ( 3. Normalisation — the
|
| Command | |
|---|---|
Format Both Sides (Shift+Alt+F) |
Pretty-print JSON / XML / HTML |
| Sort Lines on Both Sides | For comparing unordered sets — dependency lists, log lines |
| Trim Trailing Whitespace | |
| Collapse Whitespace | Squeeze runs of spaces and blank-line blocks |
| Lowercase Both Sides | Case-insensitive compare |
| Mask Noise (Timestamps, IDs…) | Blank out volatile values — see below |
| Restore Text as Pasted | Jump back to the original, however many operations later |
Swap Sides (Cmd/Ctrl+Alt+S) |
|
| Toggle Ignore Whitespace in Diffs | Flips diffEditor.ignoreTrimWhitespace |
Every operation is applied as a workspace edit, so plain undo steps back through it. Restore Text as Pasted is the coarser escape hatch: one jump back to the text as pasted.
Mask Noise
Two log lines that differ only in their timestamp are not worth diffing. Masking replaces volatile values with a constant placeholder so the lines that genuinely differ stand out:
2026-09-14T10:23:11.123Z [req 550e8400-e29b-41d4-a716-446655440000] 412ms 10.0.0.7:8080
↓
<TS> [req <UUID>] <DUR> <IP>
The picker pre-ticks only the masks that actually match the text in front of you and shows
each one's hit count, so the usual case is one Enter. Presets cover ISO timestamps, clock times,
UUIDs, epoch milliseconds, hex ids and hashes, durations, IP addresses, stack-trace line:column
and plain numbers.
For the request ids and thread names specific to your own logs, add your own in
compareTabs.maskPatterns:
"compareTabs.maskPatterns": [
{ "pattern": "trace-[0-9a-f]{16}", "replacement": "<TRACE>", "label": "Trace id" },
{ "pattern": "pool-\\d+-thread-\\d+", "replacement": "<THREAD>", "label": "Thread" }
]
On regex safety. A pattern that repeats a group which itself repeats —
(a+)+,(\d+\.)+— is rejected before it runs. JavaScript cannot time-bound a regular expression in-process: by the time you could measure that one is slow it has already frozen the editor, with no way to interrupt it. Refusing the dangerous shape up front is the only defence that runs before the damage, so the check is deliberately conservative — a few safe patterns get rejected too, and you get a message saying which and why.
4. Getting text in
- Send Selection to Paste Compare — right-click a selection in any editor. The first invocation fills the left side, the next fills the right.
- Load Side from File… and Paste Clipboard into Side
- Save Focused Side As… — writes the side you are in to a real file, with an extension suggested by the detected language.
Settings
| Setting | Default | |
|---|---|---|
compareTabs.statusBar.show |
true |
Show the status bar items |
compareTabs.statusBar.showDiffStats |
true |
Show added/removed line counts |
compareTabs.autoDetectLanguage |
true |
Detect language from pasted text |
compareTabs.detection.sampleSize |
16384 |
Characters read for detection; 0 scans everything |
compareTabs.detection.debounce |
200 |
Milliseconds of idle before re-detecting and saving |
compareTabs.format.indent |
2 |
Spaces per indent level for Format Both Sides |
compareTabs.maskPatterns |
[] |
Your own noise patterns, on top of the presets |
compareTabs.pasteCompare.reuseEmptySession |
true |
Reuse an open empty panel instead of adding another |
Languages
The UI ships in English and 简体中文, following the VS Code display language.
Development
npm test # unit tests — no install needed, node builtins only
npm install # optional: @types/vscode, for IntelliSense in the editor
Press F5 to launch an Extension Development Host.
src/extension.js |
Commands, status bar, debouncing, activation |
src/memfs.js |
The paste-compare: in-memory file system and its sessions |
src/languageRules.js |
Content-based language detection (no vscode import) |
src/normalize.js |
Formatting and text normalisation (no vscode import) |
src/diffStats.js |
Myers line-diff statistics (no vscode import) |
The three leaf modules deliberately avoid importing vscode, so they run — and are
tested — under plain node.
Packaging & Installing
npm run package # produces a .vsix
code --install-extension compare-tabs-0.1.1.vsix
Related built-in features (no extension needed)
Compare Active File with Clipboard— current file vs clipboardCompare New Untitled Text Files— two blank editors to paste into manually