feat: prefill function/arrow/def snippets on typing (issue #618) - #3095
Merged
Conversation
Adds built-in Custom Snippets that pop up as you type "function"/"arrow"
(JS/TS/JSX/TSX), "function" (PHP), or "def" (Python), inserting a bare
function/arrow-function/def skeleton with tab-stops for the name and
params, without requiring the full word to be typed first (fires on a
2+ char prefix for these built-ins only).
Architecture:
- Built-in snippets (src/extensionsIntegrated/CustomSnippets/defaultSnippets.js)
are merged into the matching engine's optimized structures at runtime
only - never written to the user's customSnippets.json and never part
of Global.SnippetHintsList, so they're invisible to the Custom
Snippets panel and can't be edited/deleted there. Nothing to keep in
sync: they're re-derived from this file fresh on every boot.
- Fixed a pre-existing bug in helper.js: snippetsByAbbreviation mapped
one abbreviation to a single snippet, so two snippets sharing an
abbreviation across languages (e.g. "function" for JS and PHP) would
silently shadow each other. Now maps to a candidate array, checked
per-language.
- Hint insertion now resolves the exact snippet via a stable
insertionKey carried on the hint element (helper.js
getSnippetByInsertionKey), rather than re-deriving language context
from the cursor at accept time - avoids redundant work and any risk
of resolving a different snippet than what was actually shown.
- Migrated Custom Snippets' tab-stop engine
(snippetCursorManager.js) onto editor/TabstopManager.js (already used
by DocCommentHints/LSP completions) to get real
select-on-tab `${N:default}` placeholders, which the old bespoke
engine couldn't support. Added escapeBareDollarSigns so every
pre-existing saved snippet keeps behaving byte-for-byte identically
under the new grammar.
- Fixed two bugs surfaced in TabstopManager.js by that migration:
goToNextStop/goToPreviousStop crashed when called with no active
session, and ending a session no longer happened when the cursor
moved outside the snippet's lines (or a multi-cursor selection was
made) - both fixed at the shared-engine level, so LSP completions and
DocCommentHints get the fix too.
Also updates the i18n rule in CLAUDE.md: only genuinely translatable
strings belong in strings.js. Content that must render identically in
every locale (code syntax, keyword examples) should be a local
constant instead, since the automated AI translation pass has no way
to tell code tokens apart from prose.
abose
force-pushed
the
feat/618-prefill-function-snippets
branch
from
August 15, 2026 16:54
01e3ba7 to
45e3ff1
Compare
The function/arrow/def default snippet bodies hardcoded 4 literal
spaces, which looked wrong the moment a file used a different width
or tabs.
Adds an INDENT_TOKEN ("@@indent@@") that templateText can use for "one
indent level, whatever this file is actually configured/detected to
use". It's resolved by snippetCursorManager.js's own preprocessing
(getOneIndentUnit/resolveIndentToken), using the same
Editor.getUseTabChar/getSpaceUnits APIs (auto-detection + project/
language preference cascade) Phoenix's own Tab-key handling relies on,
scoped per-file via the editor being inserted into.
Deliberately not new tab-stop syntax taught to editor/TabstopManager
(the shared engine LSP completions and DocCommentHints also use) -
the token is fully substituted with literal characters before the
text ever reaches that parser, so real LSP-served snippet text (which
never passes through this module) can't be affected, and there's
nothing left for TabstopManager to interpret even in principle.
Also exposed to user-authored custom snippets via the panel's
template-field tooltip, since the mechanism isn't built-in-specific.
Fixes a real bug found while writing this: `require("editor/Editor")`
needs `.Editor` to get the actual class with its static methods -
used the bare module by mistake, which would have thrown at runtime.
TabstopManager now draws a subdued box around every remaining tab-stop in an active session, with a bolder box layered on top of whichever stop is currently selected - matching the visual language RenameIdentifier.js already uses for its rename boxes. Benefits every consumer of the shared tab-stop engine (LSP completions, DocComment hints, Custom Snippets), not just one feature. Uses a new @bc-editor-decoration-neutral variable (no light/dark split) for the subdued box, since it decorates the code editor canvas whose background depends on the user's code theme, not the app's light/dark UI mode - same reasoning as .cm-matchhighlight. The active box reuses the existing @bc-primary-btn-border/@dark-bc-primary-btn-border pair split by .dark& for consistency with rename's own accent color. Adds unit coverage for marker class assignment, active-marker swap on navigation, zero-width stops correctly getting no box, and both markers clearing on session end.
…k line .custom-snippet-code-hint (the "Snippet" badge on default-snippet hint rows) was only position: absolute while its row was highlighted. When unhighlighted it fell back to static positioning with just visibility: hidden - which still reserves layout space - so the invisible badge text wrapped onto its own line inside the row, leaving a blank line and shifting every hint below it down as soon as selection moved off that row (e.g. pressing the down arrow). Make the badge position: absolute unconditionally so it's out of flow in both states; only visibility toggles with .highlight now.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Adds built-in Custom Snippets that pop up as you type "function"/"arrow" (JS/TS/JSX/TSX), "function" (PHP), or "def" (Python), inserting a bare function/arrow-function/def skeleton with tab-stops for the name and params, without requiring the full word to be typed first (fires on a 2+ char prefix for these built-ins only).
Screencast.From.2026-08-15.22-22-13.mp4
Architecture:
${N:default}placeholders, which the old bespoke engine couldn't support. Added escapeBareDollarSigns so every pre-existing saved snippet keeps behaving byte-for-byte identically under the new grammar.Also updates the i18n rule in CLAUDE.md: only genuinely translatable strings belong in strings.js. Content that must render identically in every locale (code syntax, keyword examples) should be a local constant instead, since the automated AI translation pass has no way to tell code tokens apart from prose.