Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>DreamGUI Language SupportNew to Visual Studio Code? Get it now.
DreamGUI Language Support

DreamGUI Language Support

typedreammoon

| (0) | Free
Language support for DreamGUI .dui widget hierarchies: completion, hover, outline, diagnostics, driven by symbols the Unreal plugin exports.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

DreamGUI Language Support

English | 简体中文

Language support for DreamGUI .dui widget hierarchies. DreamGUI and the language are documented at gui.toolchain.64hz.cn.

What it does

  • Syntax highlighting for the whole grammar — binding expressions (Enabled <- !IsBusy() && Count() > 0), the two-way arrow (Value <-> Volume), use "…" imports and both loop-source shapes included — plus semantic highlighting for identity: node ids render as the member variables they become, styles as classes, resources as readonly constants, -> events and handlers, loop variables (Item.Member in an each body colours as the parameter it uses), calls as functions, bare names as variables, components.
  • rows tables (rows Row : ListRow (Label, Description) { "City Ruins", "…" … }) are read as the compiler reads them: one unnamed widget per line, named from its first value, with DUI2020 / DUI3023 for a table that does not read and two rows whose keys collide. The header is highlighted and its style judged once; the formatter keeps each row on its line and the author's column alignment.
  • The component syntax, highlighted: use "Row.dui" as Row, use /Game/UI/WBP_Row as Row and use "Lib.dui" as nier; props { … } and events { … } with their type words; OnClick -> emit Picked(Index); slot Rows default : RowList { … } and a host's slot Detail { … }; containers as node types (VerticalBox Column { Spacing = 29 }) and nodes with no id (HorizontalBox { … }, Text : Caption { … }); @slot { A = 1 B = 2 }, @fill and @fill 2; namespaced names (nier.Row, : nier.Label, @nier.Ink); if … else if … else; for Item in GetItems() { … }; and timeline blocks. A one-line block (+ UIButton { TransitionType = None bCanNavigateHere = false }, Label = "MAP"; Icon = @IconMap) colours each statement as its own property.
  • Snippets for the component syntax: a component file (dui-component), props, events, emit, use-as, use-class, for, if / ifelse, slot-default, slot-fill, slotblock, shown, vbox and hbox, beside the older ones.
  • Completion for built-in tags, layout containers, component classes, per-class properties, enum values, slot properties, resource types, @ resource references and -> event names -- and for the component syntax: the aliases a file can use as node types (its own use … as lines and the ones its libraries re-export), ns. namespaces, prop and event types inside props { } / events { }, emit and this file's events after ->, @slot / @fill / @key after an @, keywords only where they are legal, and on a component instance its props, events and slots. A node typed by a container offers the container's properties too, and Shown is a widget property.
  • Colour chips and a picker on every #hex literal -- properties, styles and resources alike. The picker keeps your digit style (#F00 stays short) and never rewrites an @ reference.
  • Hover with property types and enum values, and for the component syntax: what an alias resolves to (file, class, props, events, slots), where a namespaced name comes from, an emit's signature, a prop's type and default, and the id an unnamed node compiles to. Outline of the widget tree with if / else branches, unnamed nodes by type, slot declarations and fills, loops, props, events, styles and resources. Go to definition for @Name and style uses, an alias type to its component file, a use … as name to its target, emit Picked to the events entry, a binding's prop to its props entry, ns.Label / @ns.Ink to the library, and a slot fill to the component's slot. Token-accurate folding (comment runs included) and smart selection.
  • Diagnostics with the compiler's own codes and wording, at the compiler's own severities: the six lexical codes (DUI1001–1006); the structural ones a single file settles (DUI2002/2003/2004/2006/2013, the component syntax's 2015–2019, 3001/3002/3004/3005/3014/3015/3016, 3017, 3019, 3020, 3022, 3021 where one file proves it, and 3018 for a class alias), plus as warnings the rename-clause checks (3010–3012), 3008, and what the workspace index makes certain (3004 for a namespaced style, 3018 for a file alias, 3021 from the imports, 4007); plus symbol-driven checks. The compiler stays the authority — anything it might accept is a warning here at most, and the project's real files sweep clean by test.
  • Rename and refactors know the component syntax: renaming an unnamed node writes the id after its type (no (was:), as the plugin's write-back does), an alias or namespace renames every use in the file, a prop renames its entry and every binding reading it, an event its entry and every emit. Extract-style works on unnamed nodes; inline-style carries a style's + components and slot lines over. An unknown component type (DUI3003) offers use "…" as X when exactly one component file of that name exists.
  • timeline blocks are highlighted, outlined and left alone: a track line (Row/Icon.RenderScale : 0.0 = (1, 1, 1), 0.3 = (1.25, 1.25, 1) ease InOutQuad) resolves against the widget tree and the engine's reflection, which one file's characters cannot see, so every verdict about one is the compiler's. timeline X external names an animation that lives in the asset and is edited in Sequencer.
  • Explanations: a diagnostic's code, in Problems and in the hover, is a link to its entry on the docs site (DUI1xxx–7xxx, in Chinese when VS Code runs in Chinese). Offline, every diagnostic offers an "解释 DUInnnn" action opening the bundled code-table entry — what it means, why it fired, how to fix it. The table covers the compiler's whole table, the component syntax's codes included (use … as, props, events, if, slots, for, emit and the two new write-back refusals), and is held to it by test.
  • Quickfixes: declare the unknown resource (DUI4007), create the unknown style (DUI3004).
  • A new-file command ("DreamUI: New .dui File", also on a folder's context menu) writing the same starter the Unreal editor's "Create Source File..." writes — a root plus one centred label, so the first compile answers "did this work" by appearing.
  • A status bar item showing whether symbols are loaded — a missing .dui-symbols.json is a visible warning with instructions, not a silent downgrade.
  • Workspace navigation: Ctrl+T over every id, style and resource in every .dui; F12 on a nested /Game/X tag lands on the file whose class line declares it, and F12 on a use "…" spelling opens the imported file when exactly one workspace file matches it; find-references for styles, resources and class paths.
  • Rename (F2) that speaks the language: styles and resources rename with every use; renaming a node id writes the (was: OldId) clause so the next compile migrates graph references, bindings and animations — and renaming back removes it. You are told when localization keys change and how @key pins the old ones.
  • Extract and inline: a literal becomes an @resource (one occurrence or all of them), selected property lines become a style the node wears, a worn style inlines back base-first-derived-overriding.
  • Compiler diagnostics, live: with the Unreal editor running, every compile of a text-backed class delivers its verdicts to DUI/.dui-diagnostics.json, and they appear in Problems as dui-compiler — the semantic layer (unknown properties, bad values, missing binding functions) beside the grammar-level checks, without this extension growing a second compiler. Hover shows UPROPERTY tooltips and pasteable class defaults from the widened symbols dump.
  • A bridge to the running editor: <- and -> completion ask the editor what the class really declares; typing / offers every nestable widget class (asset registry when the editor is up, the workspace's class lines when it is not); Reveal in Unreal Designer opens the designer and selects the node under the cursor; Compile This File compiles now, verdicts in Problems. The Unreal designer is this language's preview surface — the bridge just takes you there.
  • Inside binding expressions, the editor's knowledge reaches the cursor: signature help on every call (( and ,), hover on a function or variable with its type, <-> completion listing the class's variables (FieldNotify ones first — the rest are polled every frame), and Item. completion inside an each body from the source's element type.
  • Format Document (and the [dui] default formatter): indentation from block depth, one space around = and the arrows, } on its own line, blank lines folded to one — the same spelling the designer's write-back prints, so the two never fight. A guard re-lexes the result and refuses any formatting that would change a single token.
  • Add use: a style or resource that lives in exactly one other file of the workspace gets a quickfix that inserts the import, spelled the way the file already spells its imports.
  • Package paths are links: /Game/... in a tag or a string opens the asset in the running editor's Content Browser (a tag that names another .dui keeps its F12 to that file instead).
  • Reveal in VS Code from the Unreal designer's toolbar or hierarchy context menu jumps here to the node's line; a single .dui opened on its own is indexed with the rest of its DUI/ tree, so imports resolve without a folder open.

What the language deliberately does not have

These come up, and every one of them is a decision rather than a gap. The extension will not complete them, the compiler refuses them where they parse at all, and none of them is coming without somebody deciding what it should mean first.

if and for used to head this list. Both are in the language now, in the shape the rest of it allowed: if switches visibility, and for repeats one template from the class's data. What they did not bring is below.

Not in the language Why, and what to write instead
#ifdef, compile-time conditions if is a run-time switch, not a compile-time one: every branch is built, each of its widgets gets a Shown bound to "this branch is taken", and a hidden branch is collapsed, not destroyed, so it keeps its state. Nothing in a file leaves a widget out of the class. A variant that must not exist at all is a second class.
Macros, text templates Reuse is a component — a .dui with props, events and slots, placed by its use … as name — or a style, which may carry + Component blocks and slot lines as well as values. Neither is text substitution: an instance is a class, a style is a bag of lines, and both resolve by name.
Variables, theme variables resources { … } is the named-constant mechanism, and it resolves through use (under a namespace with use … as), so a palette lives in one file that every screen imports. props are a component's inputs — Blueprint variables its host sets or binds — not values the file reassigns. What a free variable would add is reassignment, which is the declarative rule again.
Media queries, pseudo-states (:hover) A state has to be driven by something, and in this framework that something is a behaviour (UUISelectable and friends already own hover, press, disabled and their transitions). A second state machine in the language would have to agree with it frame by frame.
A \| B for flag enums The grammar has no \| and adding an operator is a language change. A combination is written as the number its flags add up to — the compiler validates it against the enum (IsValidEnumValueOrBitfield), and the write-back prints it back, so the round trip is whole.
a ? b : c, a / b, Items[0] The expression language is arithmetic without division (/ belongs to paths and comments), comparison, logic and calls, because every operator added to it has to be lowered into a Blueprint graph, taught to this extension's scanner, and documented in three places. A choice or a division is a function on the class; an index is a function that takes one. For widgets, a choice is an if.
Array and dictionary literals Same reason, and there is nowhere for the value to land: no property the language addresses takes one from text. A list a for or each repeats comes from the class — a no-argument function, or a FieldNotify array of objects.
\uXXXX in strings .dui files are UTF-8. Write the character.
Nested loops, expressions in a loop body One level of repetition per template: a for or each inside another is refused (DUI5021, DUI5012). The way to nest is a component whose own file has the inner loop. Inside a loop body the only binding is the single hop Item.Member (DUI5014); anything richer belongs on the item's class.

Two things that look like gaps and are not:

  • Imported resources do become class variables. use "Palette.dui" gives this class a variable per entry, so a graph and the Class Defaults panel can see what @Accent is. A local entry of the same name shadows the imported one, exactly as @Accent resolves.
  • Imported style blocks do not. A style is a bag of values applied by name; it has no identity to own.

Where the smarts come from

The Unreal plugin writes DUI/.dui-symbols.json on every editor startup (or on demand via the console command DreamUI.ExportSymbols). This extension reads that file, so completion offers exactly what the compiler accepts — the two cannot drift, because there is only one list.

No symbols file yet? Open the project in the Unreal editor once, with a DUI/ directory present. Everything grammar-driven works without it, and the status bar says which mode you are in.

Language smarts beyond one file's characters — property types, bindings, asset paths — stay in the compiler, and reach the editor through the compiler's own diagnostics rather than a second implementation that would drift.

Build

npm install
npm test            # tsc + node --test (no VSCode, no engine needed)
npm run build       # bundles to out/extension.js
npm run package     # produces the .vsix

Point DREAMUI_CORPUS_DIR at a project's DUI/ directory to sweep every real file through the scanner and structure layer as part of npm test.

Release

Bump version in package.json, add its ## x.y.z section to CHANGELOG.md, push to master. The release workflow runs the tests, tags vx.y.z, and publishes a GitHub release carrying the .vsix with that section as its notes. With a VSCE_PAT repository secret (an Azure DevOps token with Marketplace > Manage scope for the typedreammoon publisher) it publishes the same .vsix to the Marketplace; without one that step is skipped. After adding the secret, run the workflow by hand (Actions > release > Run workflow) to publish a version that is already on GitHub.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft