Verse Magic
Syntax highlighting and digest-powered hover/autocomplete for the Verse language used in UEFN.
Features
Syntax highlighting for .verse files — comments, strings (with "{Expr}" interpolation), attributes, specifiers, control-flow keywords, declarations, function/method names (a distinct color by default — see below), field/parameter names, and primitive types (void, int, float, string, logic, etc.).
Hover documentation — hover any known symbol name to see its signature and doc comment, sourced from Epic's generated digest files. Hovering your own field/param/local names shows their declared or inferred type (plus the digest doc for that type, if it's a known one); hovering one of your own class/struct/interface/enum names shows its declaration line.
Autocomplete sourced from the same digests, plus your own file's fields/params/locals:
- Anywhere: global completions for known modules, classes, structs, interfaces, enums, and top-level functions, plus visible fields/params/locals/
Self/sibling methods in your current file.
- After a
.: member completions for a known type or module name (e.g. fort_hud_controller., Fortnite.UI.) — and, more usefully, for a variable whose declared type is known (e.g. MyEvent. where MyEvent : event(), or a for (X : ArrayField) loop variable).
Go to definition (F12 / Ctrl+click) — jumps to a field/param/local/method's declaration in the current file, to a class/field/method declared anywhere else in the workspace, or to a digest symbol's declaration line in whichever *.digest.verse file it came from.
Signature help — shows parameter info while typing inside a call's (...), for your own methods (current file or elsewhere in the workspace) and known digest functions, highlighting the active parameter as you type past each comma.
Find all references (Shift+F12) and rename symbol (F2) — for a local (param/local/for-binding), scoped to the current file; for a class/struct/interface/enum name or module-scope function, project-wide by name (these are effectively unique identifiers in Verse); for a class/enum-scoped field/method/enum-value, project-wide but verified per occurrence (a bare reference has to actually resolve to this exact declaration, a Receiver.Name reference has to have a receiver whose type actually has this member) — so a same-named member on an unrelated class is never touched. When that verification can't be done confidently, both features refuse rather than guess: rename would rather do nothing than silently corrupt unrelated code. Renaming a digest (Epic API) symbol is refused outright.
On top of the current file's own fields/params/locals, the extension indexes every other .verse file in the workspace for its classes' fields/methods, enum values, and module-scope functions — so calling into another file's class, referencing its enum's values (your_enum.SomeValue), or referencing a module-scope helper function declared elsewhere, resolves for hover/completion/definition/signature-help too, not just within the file you're editing.
Digest files (*.digest.verse) are Verse-syntax declaration dumps that Epic
generates for the public API, and that UEFN also generates for your own
project's Verse code. This extension doesn't ship any of Epic's own
generated content — those files aren't mine to redistribute — so it reads
them from wherever UEFN already writes them on your machine instead, via
two sources that merge together:
Your UEFN project's own AppData Digests folder — where UEFN itself
regenerates the global Verse/UnrealEngine/Fortnite API digests (and your
project's own digest) on every compile, typically
C:\Users\<you>\AppData\Local\UnrealEditorFortnite\Saved\VerseProject\<ProjectName>\Digests.
The extension tries to auto-detect this (see the uefnDigestsFolder
setting below) but VS Code's extension API has no real way to know which
UEFN project goes with your workspace, so auto-detection only kicks in
when it's unambiguous — otherwise set the path explicitly.
A workspace-relative digest folder (default digests) — for
manually copying or symlinking digest files into your workspace instead,
if you'd rather not point at AppData.
Settings
verse.digests.uefnDigestsFolder (default "", empty) — absolute path to
your UEFN project's generated Digests folder. Searched recursively, so it
doesn't matter whether your UEFN version lays the *.digest.verse files
flat or nests them in subfolders (older versions have been observed doing
the latter) — no need to flatten/copy them yourself either way. Leave
empty to auto-detect from the standard UEFN AppData location — this only
works when there's exactly one UEFN project on your machine, or one whose
name matches your workspace folder's name; otherwise, set this explicitly.
Windows only — UEFN doesn't currently ship elsewhere, and there's no
verified equivalent path to guess at on another platform.
verse.digests.projectFolder (default "digests") — workspace-relative folder to scan for digest files, for the manual-copy approach above.
Syntax colors
Five token kinds ship with an opinionated default color/style (function
names, field/parameter names, primitive types, attribute names, and
specifiers), set via a configurationDefaults entry for
editor.tokenColorCustomizations. Every token this grammar produces — not
just those five — is fully overridable the standard VS Code way: add a rule
for its scope name to editor.tokenColorCustomizations.textMateRules in
your own settings.json. (Note: if you already set
editor.tokenColorCustomizations yourself for anything else, your version
replaces this extension's defaults entirely rather than merging, since it's
a single setting value — copy the rows below into yours for whichever ones
you want to keep.)
| Scope |
What it colors |
Default |
entity.name.function.verse |
Function/method names, at their declaration or a call site |
#CC6633 (burnt orange) |
variable.other.member.verse |
Field/parameter names before a : type annotation |
#9CDCFE (light blue) |
support.type.primitive.verse |
Primitive types — void, int, float, string, logic, etc. |
#4EC9B0 (teal) |
entity.other.attribute-name.verse |
Attribute names, e.g. editable in @editable |
#DCDCAA (gold) |
storage.modifier.verse |
Specifiers, e.g. <native>, <public>, <transacts> |
italic (theme's own color) |
entity.name.type.verse |
Class/struct/interface/enum/type declaration names |
theme default |
storage.type.verse |
Declaration keywords — module, class, enum, var, type, using, etc. |
theme default |
keyword.control.verse |
Control-flow keywords — if, for, loop, spawn, race, etc. |
theme default |
keyword.operator.verse |
Operators — :=, .., comparisons, etc. |
theme default |
constant.language.verse |
true, false, external |
theme default |
constant.numeric.verse |
Numeric literals |
theme default |
entity.name.namespace.verse |
Qualified paths, e.g. /Fortnite.com/UI |
theme default |
string.quoted.double.verse |
String literals |
theme default |
constant.character.escape.verse |
Escape sequences inside strings |
theme default |
meta.embedded.interpolation.verse |
The Expr part of a "{Expr}" string interpolation |
theme default |
punctuation.definition.template-expression.begin.verse / ...end.verse |
The { / } of a string interpolation |
theme default |
punctuation.definition.attribute.verse |
The @ of an attribute |
theme default |
variable.parameter.verse |
Identifiers inside an attribute's {...} arguments |
theme default |
comment.line.number-sign.verse / comment.block.verse |
# line comments / <# #> block comments |
theme default |
punctuation.verse |
Brackets, braces, commas, colons, semicolons |
theme default |
Rows marked "theme default" don't get an explicit color from this
extension — they use standard, widely-recognized TextMate scope names
(comment, keyword.control, etc.), so most themes already color them
sensibly without any help. They're just as overridable as the five with
explicit defaults; add your own textMateRules entry for any of them.
Troubleshooting
.verse files open as Plain Text no matter what's installed or enabled.
Check files.associations in your User and Workspace settings
(Command Palette → Preferences: Open Settings (UI), search
files.associations) for an entry like "*.verse": "Verse". That value
has to be the exact, case-sensitive language id — this extension
registers "verse" (lowercase); "Verse" is only a display alias (what
shows in the language picker), not a usable association target. A
mismatched-case entry silently overrides every extension's own
language contribution, which is why it happens regardless of what's
installed. Fix: change the value to lowercase "verse".
Does this work alongside Epic's official Verse extension?
Yes, both can be installed and enabled at the same time.
Pay-what-you-like
- If you find this extension useful, I have a ko-fi page where you can show your appreciation - every little helps :) https://ko-fi.com/markripley
| |