HXPS EDGECAM Postprocessor Toolset
Syntax highlighting, code navigation, hover documentation, completion, and diagnostics
for EDGECAM post-processor sources.
Supported file types: .cgs · .cgi · .mxt · .cxt
Features
Hover
| Hover over |
You get |
POSITION, ANGLE, … |
numeric system variable and its modifier number |
TOOLDESCRIPTION, … |
string system variable and what it holds |
STRMID, SIN, … |
the function's signature and behaviour |
the number on %MACRO=15=… |
what that number means in every discipline |
#MyVar / $MyStr |
which %DECLARE introduces it, and in which layer |
!FT_XMOVE |
the %FORMAT line it resolves to |
${CYCL/BORING} |
the msgctxt key it looks up in the PO catalogues |
%CALL=Name |
every definition in the chain, and which one compiles in |
A macro number always shows all disciplines that define it. That is deliberate:
15 is TOOLCHANGE in Mill and Turn but 165 in C Axis Turn, and 3 is MOVE ARC in
Mill but SIMPLE TURN CYCLE in Turn. A number alone is not an answer.
Code navigation
| Cursor on |
F12 jumps to |
%CALL=ProcName |
%PROCEDURE=ProcName, winning definition first |
@LABEL |
the label in the same macro or procedure |
#MyVar / $MyStr |
the %DECLARE line |
!FT_NAME |
the %FORMAT line whose trailing comment names it |
Override order comes from the post's %INCLUDE chain — later wins — not from filename
prefixes, so Common.cgi and GLOBAL_* sources rank correctly. Shift+F12 finds
references; %INCLUDE= paths are clickable.
The EDGECAM template layer
Template code is baked into the .cgd and appears in no .cgi — only in the compiled
.mxt/.cxt. In one real post that is 972 %DECLARE lines and 763 procedure
definitions that a source-only search can never see. Since that code has to be read
and investigated regularly, navigation reaches into compiled output:
F12 falls back to the .mxt/.cxt for procedures and variables.
#MAXSUBNUMBER resolves to its %DECLARE there instead of coming up empty.
Shift+F12 appends compiled hits after the source ones. For a template variable
that is the only place there are any: #MAXSUBNUMBER is declared at .mxt:939, set
at 3402 and tested at 28378 and 30613, with no source occurrence at all.
- Hover reads the template's own documentation. Compiled posts carry
;Setvar^
lines describing each configurable template variable, so hovering #MAXSUBNUMBER
gives "Maximum Allowed Subroutine Number — category Subroutines · type INT ·
default 254", and a LIST variable such as #USERADS gives its allowed values
(0 Never · 1 Except 360 or Helical · …). Undocumented in the official help, so it
is labelled as read from the compiled file.
edgecam-pp.searchCompiledOutput (default on) controls the reference search. Go to
Definition falls back regardless — otherwise a template variable would have no
reachable definition at all.
If a template symbol is not found, the post has probably not been compiled yet: those
declarations only become visible once the .mxt/.cxt exists.
Template awareness — what this post does not compile in
Generic sources serve many machines, and ;IF TEMPLATE= guards decide which parts
reach any given post. Those regions are faded, the way VS Code fades unreachable
code, so a shared file reads at a glance:
;IF TEMPLATE=3AXMILL^4AXMILL^5AXMILL ← normal: this post is 5AXMILL
%PROCEDURE=VS_IS_HOLE_CYCLE
…
%ENDM
;ENDIF
;IF TEMPLATE=2AXLATHE^2CYLATHE^… ← faded: not compiled into this post
%PROCEDURE=VS_IS_HOLE_CYCLE
…
%ENDM
;ENDIF
F12 puts the variant this post actually gets first, and hover names the rest with
(not in this post) — nothing is hidden, because reading the variant another machine
gets is ordinary work.
The template compared against comes from the compiled .mxt/.cxt, not from the
.cgs. A .cgs declares an alias (AUTOMILL, AUTOLATHE) that matches no guard;
the compiled file's first line carries the resolved name (5AXMILL, 2CYLATHE, …).
So this only works once the post has been compiled — before that nothing is faded,
because "unknown" must not look like "dead". In a generic source shared by several
posts, a region is faded only where it is inactive for all of them.
Switch it off with edgecam-pp.diagnostics.dimInactiveTemplateBlocks.
No colour swatches on variables
VS Code's built-in colour provider reads #RGB, #RGBA, #RRGGBB and #RRGGBBAA as a
colour literal in any language — and post-processor variable names collide with that
badly. #FEED is four hex digits, so the feedrate variable would render as a colour swatch
for #FFEEEEDD; in one production generic tree alone that is 1,079 occurrences, plus
37 other hex-like names such as #dec and #530.
The extension declares that this language has no colour syntax, which suppresses the
built-in provider, and ships editor.colorDecorators: false scoped to [edgecam-pp] for
the case where colour decorators are forced on globally.
Completion
Context-aware: procedures after %CALL=, labels of the current block after @,
declared variables after #/$, format references after !, directives after %,
and system variables and functions elsewhere.
Diagnostics
Switchable under edgecam-pp.diagnostics.*:
- structural — unclosed blocks, nested definitions,
%DECLARE inside a macro, a
%GOTO whose label is not in the same block, duplicate labels, identifiers over
29 characters, an unterminated %ELSEIF chain, a nested %IF.
- limits — more than 100 labels in a block (
TOO MANY LABELS).
- crossFile — unresolvable
%CALL targets, a %CALL aimed at a %MACRO instead
of a %PROCEDURE, missing include files, definitions shadowed later in the chain,
and the two rules below.
- variantOverrides — the override that has no name collision. A dispatcher shim
decides which
<PREFIX>_<BASE> variant runs with one hardcoded %CALL= line, so
when CODECON_THREAD_CYCLE calls PRIVATE_THREAD_CYCLE, GENERIC_THREAD_CYCLE
still compiles in and nothing reaches it. Reported on the bypassed definition; the
mirror case — a PRIVATE_/CUST_ variant written but never routed to — is a
warning, because that is the two-step edit half-done.
- unreachableProcedures — dead code, faded. EDGECAM only ever invokes
%MACRO
blocks, so the macros in the compiled post are the roots, %CALL= is the only edge,
and any procedure outside that closure is unreachable however plausible its name.
Transitive: on one real post PT_PROBE_OFF does have a caller, but that caller is
PT_PROBE_MODE, which nothing reaches — the whole probing subtree is dead there.
Only files this post owns are judged, and nothing is reported until the post has been
compiled.
- practice — multi-line
%IF, and UNVAR/USVAR slots read after a %CALL.
Hints, never warnings.
Two things are deliberately not reported by default, because they fire on code that
compiles and ships:
- the documented 100-jump ceiling (
tooManyJumps) — six blocks in one production
generic tree exceed it, the busiest at 158;
#USERn usage (preferDeclareOverUser) — existing sources use those slots
pervasively, and the guidance is to leave that code alone unless you are changing the
block anyway.
Dead code
A %PROCEDURE is only ever reached by a %CALL, and EDGECAM itself only ever invokes
%MACRO blocks. That makes reachability decidable rather than a guess: take the macros
in the compiled post as roots, follow %CALL= edges, and whatever is left over is dead.
The compiled .mxt/.cxt is what gets walked, because it is the whole merged program —
the template layer's macros are the roots, and they exist in no .cgi.
Measured over 40 real posts: 524 unreachable procedures in post-owned files, a median of
about 13 per post. Three turning posts account for 46 each — a full turning
CODECONS.CGI was copied in and only part of it wired up, so CODECON_BARPULL,
CODECON_SUBSPIN_RETRACT and CODECON_ATC_GEN-TOOLCH sit there with a definition, a
debug label and no caller at all. The same CODECON_TAILSTOCK is dead in those posts
and live in a fourth that calls it twice, so the verdict is per post, never a blanket
claim about a name.
Two limits keep it honest:
- A shared source is never judged. A generic library procedure this machine does not
use is the normal state of shared code; across those 40 posts that is 2,822
unreachable procedures in shared files against the 524 in post-owned ones.
- Nothing is reported before the post has been compiled, for the same reason
template fading waits: "cannot say" must not read as "dead".
Where the variant rules already have something to say — a bypassed or unrouted variant
is unreachable too — that more specific marker is the only one shown, because it also
names the variant that runs instead.
Outline, folding, Compare Definitions
%PROCEDURE, %MACRO (shown as e.g. Toolchange (15)), %MACHINE and %FORMAT
appear in the Outline, with their labels as children — matching the fact that a label
belongs to its block and nowhere else. %COMMAND blocks and ;IF TEMPLATE= regions
fold.
A ↕ Compare definitions CodeLens appears above any procedure another file in the
post also defines, and opens a diff of the two bodies.
A second ↕ Compare with … CodeLens appears where the sibling has a different
name — GENERIC_THREAD_CYCLE against PRIVATE_THREAD_CYCLE — which is how most
overrides actually look. The routed variant is on the right, so the diff reads the same
way round as the same-name one. On one real 5-axis mill post the two bodies are 88 and
89 lines and differ in exactly one: the CYCLE70 retract plane.
Reference counts
Above every %PROCEDURE sits a 3 references CodeLens, the way Visual Studio puts
one above a C# member. Clicking it opens the peek window on those call sites.
Only procedures get one: a %MACRO is invoked by EDGECAM itself, not by %CALL, so a
count on one would always read zero and mean nothing.
Where a procedure has no %CALL in the sources but the compiled post does call it, the
count says 1 reference (template) — that is the EDGECAM template layer, and it is
the normal state of a CODECON_* dispatcher: 66 of the 89 in one real CODECONS.CGI
are reached that way and from no .cgi at all. The two are never added together, because
the compiled file is the merged program and already contains each source call once;
summing them would double every count. And 0 references is rendered as plain text
rather than a link, since there is nothing to peek at — on GENERIC_CODECONS.CGI that
is 11 of 112 procedures, GENERIC_THREAD_CYCLE among them, which is the same finding
the variant rule reports from the other side.
Compiled posts get counts too, and this is where they are hardest to come by
otherwise: the template layer exists only in the .mxt/.cxt, and nothing else tells
you who calls one of its procedures. There the count is scoped to that one file, because
a compiled post is one whole post — 603 lenses on a real 30,631-line .mxt, with
ENTER_PROCEDURE at 345 references and PT_BINC at 346. No (template) suffix
appears, since every call in such a file is the compiled program's. Compare Definitions
stays absent there: the compiler resolved every override before writing the file, so it
holds one definition per name and there is no second body to diff.
Snippets
For .cgs, .cgi, .po, .ini and JavaScript.
A note on multi-line %IF
The language has three conditional shapes:
%IF #X=1 %THEN #Y=1 %ELSE #Y=2 ; single line - the safe one
%IF $W="a" %THEN ; continuation: body on the next line,
$W="b" : %GOTO @done ; bound by INDENTATION
%IF #P=1 %THEN ; %ELSEIF chain
#A=1
%ELSEIF #P=2 %THEN
#A=2
%ENDIF
The last two are highlighted so existing code reads properly, but they get no folding
and no auto-indent, and the practice tier hints against them. They depend on
indentation to bind their body and cannot be nested, so a formatter that touched the
whitespace could change the program. %GOTO branching is the safe form.
Post-processor layout
{POSTNAME}.cgs — the include list; its ORDER decides what overrides what
{POSTNAME}.mxt / .cxt — compiled output (read-only; the only place the
EDGECAM template layer is visible)
CGI/
PRIVATE_CODECONS.cgi — this machine only
CODECONS.cgi
…/<customer library>/…
CUST_CODECONS.cgi — this customer's posts
…/<generic tree>/…
Common.cgi — shared, no prefix
GENERIC_CODECONS.cgi — every post that includes it
Precedence is the include order: a later %INCLUDE overrides an earlier one. Layer
names (GENERIC, CUST, GLOBAL, PRIVATE) are shown for orientation, but the chain
is what decides.
Requirements
VS Code ^1.75.0