HopOutMove outside the nearest enclosing quote, bracket or paren. VS Code has
Keys and commands
All of them work with multiple cursors. With How
|
| Setting | Default | |
|---|---|---|
hopOut.enabled |
true |
Let Tab hop. Per-language: "[markdown]": { "hopOut.enabled": false }. |
hopOut.pairs |
( ) [ ] { } " " ' ' ` ` |
Pairs written as open close. Multi-character delimiters are fine: "<!-- -->", "/* */", "{% %}". A single token is used on both sides: "$", "**". Per-language overridable. |
hopOut.languageAwarePairs |
true |
Adds < > in HTML/XML/Vue/Svelte/JSX, emphasis and math delimiters in Markdown, triple quotes in Python/TOML/Elixir/Julia, math delimiters in LaTeX; stops treating ' as a quote in Rust, OCaml, F# and Lisps. |
hopOut.tab.trigger |
adjacent |
adjacent: hop only when the closer is directly ahead. line: hop from anywhere inside a pair that closes on this line, e.g. the middle of a string. enclosing: hop from anywhere inside a pair, even one that closes on a later line. |
hopOut.tab.skipWhitespace |
true |
{ a: 1| } counts as directly ahead. |
hopOut.tab.fromLineEnd |
false |
At the end of a line, hop past a closer that starts a following line (from the last statement of a block to after its }). |
hopOut.tab.chain |
false |
Hop past all consecutive closers at once: "}) in one press. |
hopOut.tab.alsoHopOver |
[] |
Extra text to hop over when directly ahead, e.g. [";", ","]. |
hopOut.tab.priorityOverSnippets |
false |
During a snippet, prefer hopping over jumping to the next placeholder. |
hopOut.shiftTab.undoHop |
true |
Shift+Tab undoes the last hop. |
hopOut.maxScanLines |
500 |
Lines examined before and after the cursor. |
To insert a real tab where HopOut would hop, bind the built-in tab command to a key of your choice, or run HopOut: Toggle Tab Hopping.
Why another tab-out extension
HopOut was written after reading the source and issue trackers of the existing ones. Each row is a reported problem and what HopOut does about it.
| Problem in existing extensions | Reported in | HopOut |
|---|---|---|
Works on single characters, not pairs: jumps into strings and tags, treats ., :, = as stops, cannot tell an opening quote from a closing one |
TabOut #5, #24 | Scans the code structure: quotes with escapes, nested brackets, comments. Only ever hops out. |
| Cannot leave a string unless the cursor touches the closing quote | TabOutX #3 | Ctrl+' from anywhere; hopOut.tab.trigger: "line" puts it on Tab. |
| Current line only | TabOut #47 | Explicit commands cross lines; fromLineEnd and enclosing do so for Tab. |
No multi-character delimiters (*/, -->, $$, \]) |
TabOut #57, #21, #27 | Any length; block comments and Markdown/LaTeX delimiters built in. |
| Breaks snippet placeholders (regressed again in 2026) | TabOut #58, #4, #10; the opposite wish in #46 | Yields while the snippet has a next placeholder. priorityOverSnippets for the other camp. |
Steals Tab from Copilot suggestions and Next Edit Suggestions |
TabOut #31, #52, #54; TabOutX #2; Leaper #19 | Yields to inline suggestions, inline edits (accept and jump) and Cursor Tab. |
| Breaks suggestion acceptance, tab completion, Emmet, indentation | TabOut #51, #16, #9, #5 | Every Tab press used to go through the extension, which then tried to re-create the default. HopOut is not involved unless a hop is possible. |
Tab lags over SSH/WSL, or fires seconds late |
TabOut #37, #29 | Runs in the local (UI) extension host even in remote windows, and ordinary Tab presses never reach it. |
| No multiple cursors | TabOut #7 (still excluded by its keybinding); TabOutX | Supported in every command. |
Shift+Tab hijacks outdent on any line containing a "special" character |
TabOut 0.2.4 source; #12 | Shift+Tab only undoes a hop that just happened. |
| Only pairs typed in this session are known; nothing in existing code, pasted code or code inserted by completions | Leaper README, #15 | Reads the document, so where the pair came from is irrelevant. |
One press per delimiter in "}) |
TabOut #8 | hopOut.tab.chain. |
| Toggle is forgotten on restart; TabOutX reads its settings once at startup | TabOut and TabOutX source | Toggle writes the setting; all settings apply immediately. |
| No per-language configuration | enabled, pairs and the tab.* settings can be overridden per language. |
|
| No entry point for VS Code for the Web | TabOut, TabOutX and Leaper manifests | No Node dependencies and a browser entry point (built, but not yet exercised in a web host). |
What it does not do
HopOut uses a fast structural scan, not a full parser for each language, so:
- Syntax it cannot see, such as regular expression literals containing quotes, heredocs, or a string nested in the interpolation of a string with the same quote, can make it miss a pair. A missed pair means
Tabindents; a closing bracket directly ahead is always hopped over regardless. - The context key that routes
Tabfollows the cursor a few milliseconds behind. A macro that types a bracket and pressesTabin the same instant, or an extension host that is busy at that moment, gets a normal indent instead of a hop. That is the price of never delaying an ordinaryTab. - With
emmet.triggerExpansionOnTab, an abbreviation sitting directly in front of a closing delimiter is hopped past instead of expanded. Accepting it from the suggestion list works. - When another extension binds
Tabin the same context (Markdown All in One in lists, for example), VS Code decides which one wins. Disable HopOut for that language if it gets in the way. - AI tools that take
Tabthrough their own context keys, other than Copilot and Cursor, are unknown to the keybinding. Add their key to thewhenclause ofhopOut.tabin Keyboard Shortcuts.
Coming from TabOut
Uninstall or disable TabOut, TabOutX or Leaper so two extensions do not compete for Tab. tabout.charactersToTabOutFrom entries like { "open": "[", "close": "]" } become "[ ]" in hopOut.pairs; single characters such as ; or , go in hopOut.tab.alsoHopOver.
Development
npm install
npm test # unit tests for the scanning engine
npm run test:integration # runs inside an installed VS Code with a throwaway profile, pressing real keys
npm run package # builds the .vsix
src/engine.ts is the scanner and has no VS Code dependency. src/languages.ts holds the per-language comment syntax and pair adjustments. src/extension.ts keeps the context keys in sync and implements the commands.
License
MIT