@duet3d/monacotokens 3.7.0-alpha.9 → 3.7.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -5
- package/dist/expressions/expressions.json +7 -3
- package/dist/gcodes/gcodes.json +1923 -1391
- package/dist/gcodes/index.d.ts +24 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/monaco-gcode.js +20 -5
- package/dist/objectmodel/axes.d.ts +12 -0
- package/dist/objectmodel/axes.js +18 -0
- package/dist/providers.js +179 -33
- package/package.json +14 -7
package/dist/gcodes/index.d.ts
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
* One enumerated value of a parameter (e.g. "1" for G1 H meaning "Stop on endstop")
|
|
3
3
|
*/
|
|
4
4
|
export interface GcodeParameterValue {
|
|
5
|
-
/**
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Literal value as it appears in source (e.g. "0", "1", "-1"). An array lists equivalent literals that mean
|
|
7
|
+
* the same thing, e.g. M669 K accepts both the kinematics name and its legacy number ("coreXY" and "1").
|
|
8
|
+
*/
|
|
9
|
+
value: string | string[];
|
|
7
10
|
/** Human-readable meaning */
|
|
8
11
|
description: string;
|
|
9
12
|
}
|
|
@@ -31,6 +34,23 @@ export interface GcodeUnprecedentedParameter {
|
|
|
31
34
|
/** Description shown in the tooltip when this position is active */
|
|
32
35
|
description: string;
|
|
33
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Description of a parameter that exists once per machine axis (e.g. the per-axis speed limits of M203). Instead of
|
|
39
|
+
* listing X/Y/Z/U/... explicitly, a code carries a single `axisParameter` that the completion provider expands into
|
|
40
|
+
* one `GcodeParameter` per axis at runtime, so the suggestion list matches the machine that is actually connected.
|
|
41
|
+
*/
|
|
42
|
+
export interface GcodeAxisParameter {
|
|
43
|
+
/** Description shown for each generated axis parameter. The placeholder `{axis}` is replaced with the axis letter. */
|
|
44
|
+
description: string;
|
|
45
|
+
/** Optional enumeration of valid values, shared by every generated axis parameter */
|
|
46
|
+
values?: GcodeParameterValue[];
|
|
47
|
+
/**
|
|
48
|
+
* Whether the parameter list follows the live machine configuration. Defaults to `true`: expand to the axes the
|
|
49
|
+
* connected machine reports (falling back to the standard letters when no machine is connected). Set to `false`
|
|
50
|
+
* for axis-defining codes (e.g. M584, M669) that must offer every standard axis letter regardless of configuration.
|
|
51
|
+
*/
|
|
52
|
+
dynamic?: boolean;
|
|
53
|
+
}
|
|
34
54
|
/**
|
|
35
55
|
* Description of one G/M/T-code (RRF dialect)
|
|
36
56
|
*/
|
|
@@ -45,6 +65,8 @@ export interface GcodeInfo {
|
|
|
45
65
|
unprecedentedParameter?: GcodeUnprecedentedParameter;
|
|
46
66
|
/** Parameter letters this code understands */
|
|
47
67
|
parameters: GcodeParameter[];
|
|
68
|
+
/** Per-axis parameter expanded into one entry per configured axis at completion time (e.g. M203 speed limits) */
|
|
69
|
+
axisParameter?: GcodeAxisParameter;
|
|
48
70
|
/** If set, this code is deprecated; the string is shown as the reason */
|
|
49
71
|
deprecated?: string;
|
|
50
72
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./monaco-menu";
|
|
|
4
4
|
export * from "./gcodes";
|
|
5
5
|
export * from "./expressions";
|
|
6
6
|
export * from "./objectmodel/machine-context";
|
|
7
|
+
export * from "./objectmodel/axes";
|
|
7
8
|
export * from "./gcodes/local-variables";
|
|
8
9
|
export * from "./objectmodel/deprecations";
|
|
9
10
|
export * from "./objectmodel/enums";
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./monaco-menu";
|
|
|
4
4
|
export * from "./gcodes";
|
|
5
5
|
export * from "./expressions";
|
|
6
6
|
export * from "./objectmodel/machine-context";
|
|
7
|
+
export * from "./objectmodel/axes";
|
|
7
8
|
export * from "./gcodes/local-variables";
|
|
8
9
|
export * from "./objectmodel/deprecations";
|
|
9
10
|
export * from "./objectmodel/enums";
|
package/dist/monaco-gcode.js
CHANGED
|
@@ -24,9 +24,12 @@ function generateMonarchLanguage(fdmMode) {
|
|
|
24
24
|
// G53 acting as a same-line prefix (e.g. "G53 G1 X10") - highlight as a modifier, distinct from both
|
|
25
25
|
// line numbers (type) and regular codes (keyword)
|
|
26
26
|
[/[gG]53(?=[\s]+[gGmMtT][0-9-])/, "regexp", "afterG53"],
|
|
27
|
-
// G/M/T-codes (including bare G53 on its own line, which behaves like a regular command)
|
|
27
|
+
// G/M/T-codes (including bare G53 on its own line, which behaves like a regular command).
|
|
28
|
+
// M-codes get their own state because T is a valid M-code parameter letter, whereas inside
|
|
29
|
+
// a G-code's parameter list T starts a new T-code on the same line
|
|
28
30
|
[/[gG][0123](?=\D)/, "keyword", fdmMode ? "normalGcode" : "moveGcode"],
|
|
29
|
-
[/[
|
|
31
|
+
[/[gG]\d+(\.\d+)?/, "keyword", "normalGcode"],
|
|
32
|
+
[/[mM]\d+(\.\d+)?/, "keyword", "normalMcode"],
|
|
30
33
|
[/[tT](?=\{)/, "keyword", "normalGcodeWithT"],
|
|
31
34
|
[/[tT]-?\d+/, "keyword", "normalGcodeWithT"],
|
|
32
35
|
// meta keywords
|
|
@@ -52,10 +55,12 @@ function generateMonarchLanguage(fdmMode) {
|
|
|
52
55
|
afterG53: [
|
|
53
56
|
// whitespace between the G53 prefix and the following G/M/T-code
|
|
54
57
|
[/[ \t]+/, ""],
|
|
55
|
-
// next G/M/T-code - replace this state with the code's own state so its parameters
|
|
56
|
-
//
|
|
58
|
+
// next G/M/T-code - replace this state with the code's own state so its parameters are
|
|
59
|
+
// parsed normally; M-codes switch to normalMcode where T is treated as a parameter letter,
|
|
60
|
+
// while G-codes switch to normalGcode where a trailing T starts a new T-code
|
|
57
61
|
[/[gG][0123](?=\D)/, { token: "keyword", switchTo: fdmMode ? "@normalGcode" : "@moveGcode" }],
|
|
58
|
-
[/[
|
|
62
|
+
[/[gG]\d+(\.\d+)?/, { token: "keyword", switchTo: "@normalGcode" }],
|
|
63
|
+
[/[mM]\d+(\.\d+)?/, { token: "keyword", switchTo: "@normalMcode" }],
|
|
59
64
|
[/[tT](?=\{)/, { token: "keyword", switchTo: "@normalGcodeWithT" }],
|
|
60
65
|
[/[tT]-?\d+/, { token: "keyword", switchTo: "@normalGcodeWithT" }]
|
|
61
66
|
],
|
|
@@ -82,11 +87,21 @@ function generateMonarchLanguage(fdmMode) {
|
|
|
82
87
|
{ include: "gcode" }
|
|
83
88
|
],
|
|
84
89
|
normalGcode: [
|
|
90
|
+
// T inside a G-code parameter list starts a new T-code (T is not a valid G-code parameter)
|
|
91
|
+
[/[tT](?=\{)/, "keyword", "normalGcodeWithT"],
|
|
92
|
+
[/[tT]-?\d+/, "keyword", "normalGcodeWithT"],
|
|
85
93
|
// include normal gcode
|
|
86
94
|
{ include: "gcode" },
|
|
87
95
|
// EOL
|
|
88
96
|
[/\n/, "", "@popall"]
|
|
89
97
|
],
|
|
98
|
+
normalMcode: [
|
|
99
|
+
// T is a parameter letter inside an M-code, not a new T-code - so this state intentionally
|
|
100
|
+
// omits the T-code re-entry rules that normalGcode has
|
|
101
|
+
{ include: "gcode" },
|
|
102
|
+
// EOL
|
|
103
|
+
[/\n/, "", "@popall"]
|
|
104
|
+
],
|
|
90
105
|
normalGcodeWithT: [
|
|
91
106
|
// already had a T parameter, starting a new T-code
|
|
92
107
|
[/(?=T)/, "keyword", "@popall"],
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported axis letters, mirroring `Axis.Letters` in DuetAPI (DuetSoftwareFramework). This is the canonical set
|
|
3
|
+
* of identifiers RepRapFirmware accepts for an axis. Kept in sync by hand - update it if DuetAPI gains new letters.
|
|
4
|
+
*/
|
|
5
|
+
export declare const ALL_AXIS_LETTERS: readonly string[];
|
|
6
|
+
/**
|
|
7
|
+
* Axis letters offered by completion when the live machine configuration is unavailable (no machine connected) or
|
|
8
|
+
* when an axis parameter is non-dynamic (axis-defining codes such as M584 that may reference axes which do not
|
|
9
|
+
* exist yet). Restricted to the three axes every standard machine has; additional axes (U, V, W, ...) are surfaced
|
|
10
|
+
* dynamically from the connected machine's object model, and any axis can still be typed manually.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_AXIS_LETTERS: readonly string[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported axis letters, mirroring `Axis.Letters` in DuetAPI (DuetSoftwareFramework). This is the canonical set
|
|
3
|
+
* of identifiers RepRapFirmware accepts for an axis. Kept in sync by hand - update it if DuetAPI gains new letters.
|
|
4
|
+
*/
|
|
5
|
+
export const ALL_AXIS_LETTERS = [
|
|
6
|
+
"X", "Y", "Z",
|
|
7
|
+
"U", "V", "W",
|
|
8
|
+
"A", "B", "C", "D",
|
|
9
|
+
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
|
|
10
|
+
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* Axis letters offered by completion when the live machine configuration is unavailable (no machine connected) or
|
|
14
|
+
* when an axis parameter is non-dynamic (axis-defining codes such as M584 that may reference axes which do not
|
|
15
|
+
* exist yet). Restricted to the three axes every standard machine has; additional axes (U, V, W, ...) are surfaced
|
|
16
|
+
* dynamically from the connected machine's object model, and any axis can still be typed manually.
|
|
17
|
+
*/
|
|
18
|
+
export const DEFAULT_AXIS_LETTERS = ["X", "Y", "Z"];
|
package/dist/providers.js
CHANGED
|
@@ -1,12 +1,133 @@
|
|
|
1
|
-
import { gcodeData, findGcode } from "./gcodes";
|
|
1
|
+
import { gcodeData, findGcode as findGcodeStatic } from "./gcodes";
|
|
2
2
|
import { expressionData } from "./expressions";
|
|
3
3
|
import { getMachineContext } from "./objectmodel/machine-context";
|
|
4
|
+
import { ALL_AXIS_LETTERS, DEFAULT_AXIS_LETTERS } from "./objectmodel/axes";
|
|
4
5
|
import { getLocalVariables } from "./gcodes/local-variables";
|
|
5
6
|
import { getMemberDeprecation, getPathDeprecation } from "./objectmodel/deprecations";
|
|
6
7
|
import { getEnumValuesForPath } from "./objectmodel/enums";
|
|
7
8
|
// Re-export the runtime-context helpers so consumers (Vue DWC, React DuetWebUI, ...) can install a context
|
|
8
9
|
// without adding a separate import path
|
|
9
10
|
export { getMachineContext, onMachineContextChange } from "./objectmodel/machine-context";
|
|
11
|
+
/**
|
|
12
|
+
* Expand a code's `axisParameter` into one concrete `GcodeParameter` per axis letter. Dynamic parameters (the
|
|
13
|
+
* default) follow the connected machine's configured, visible axes and fall back to the standard letters when no
|
|
14
|
+
* machine is connected; non-dynamic parameters always offer the standard letters, for axis-defining codes such as
|
|
15
|
+
* M584/M669 that may reference axes which do not exist yet.
|
|
16
|
+
*/
|
|
17
|
+
function expandAxisParameter(axisParameter) {
|
|
18
|
+
let letters = DEFAULT_AXIS_LETTERS;
|
|
19
|
+
if (axisParameter.dynamic !== false) {
|
|
20
|
+
const axes = getMachineContext()?.model?.move?.axes;
|
|
21
|
+
const configured = axes
|
|
22
|
+
? axes.filter(a => !!a && a.visible !== false && typeof a.letter === "string" && a.letter.length === 1).map(a => a.letter)
|
|
23
|
+
: [];
|
|
24
|
+
if (configured.length > 0) {
|
|
25
|
+
letters = configured;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return letters.map(letter => ({
|
|
29
|
+
letter,
|
|
30
|
+
description: axisParameter.description.replace(/\{axis\}/g, letter),
|
|
31
|
+
values: axisParameter.values
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a static code entry against the live machine context, expanding any `axisParameter` into concrete
|
|
36
|
+
* per-axis parameters placed ahead of the fixed parameters. Returns the entry unchanged when it has no axis
|
|
37
|
+
* parameter, so every caller can treat the result like a plain `GcodeInfo`.
|
|
38
|
+
*/
|
|
39
|
+
function findGcode(code) {
|
|
40
|
+
const info = findGcodeStatic(code);
|
|
41
|
+
if (!info || !info.axisParameter) {
|
|
42
|
+
return info;
|
|
43
|
+
}
|
|
44
|
+
return { ...info, parameters: [...expandAxisParameter(info.axisParameter), ...info.parameters] };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Canonicalise a parameter/axis token to the letter RepRapFirmware actually uses. RRF folds bare command
|
|
48
|
+
* letters to upper case, so `a` addresses axis `A`; a leading apostrophe escapes a lower-case axis, so `'a`
|
|
49
|
+
* addresses axis `a`. The token passed in may carry that leading apostrophe.
|
|
50
|
+
*/
|
|
51
|
+
function canonicalAxisLetter(token) {
|
|
52
|
+
return token.startsWith("'") ? token.slice(1).toLowerCase() : token.toUpperCase();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Render a canonical parameter letter the way it must be written in G-code: a lower-case axis needs the leading
|
|
56
|
+
* apostrophe (otherwise RRF would fold it to upper case), every other letter is written as-is.
|
|
57
|
+
*/
|
|
58
|
+
function gcodeAxisToken(letter) {
|
|
59
|
+
return /[a-z]/.test(letter) ? "'" + letter : letter;
|
|
60
|
+
}
|
|
61
|
+
/** Build the per-axis parameter for one axis letter from a code's axisParameter, or undefined when the letter is not a supported axis or the code has no axis parameter. */
|
|
62
|
+
function makeAxisParameter(info, letter) {
|
|
63
|
+
if (!info.axisParameter || !ALL_AXIS_LETTERS.includes(letter)) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
letter,
|
|
68
|
+
description: info.axisParameter.description.replace(/\{axis\}/g, letter),
|
|
69
|
+
values: info.axisParameter.values
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve a single parameter for hover and signature use from a canonical letter (see {@link canonicalAxisLetter}).
|
|
74
|
+
* Matches a fixed parameter or an axis already expanded for the connected machine, and otherwise builds the
|
|
75
|
+
* parameter on the fly for any supported axis letter - so hovering or typing an axis the machine is not configured
|
|
76
|
+
* with still surfaces its documentation. This is wider than the completion suggestion list and the parameter
|
|
77
|
+
* summary, which only offer the configured axes.
|
|
78
|
+
*/
|
|
79
|
+
function lookupParameter(info, letter) {
|
|
80
|
+
const found = info.parameters.find(p => p.letter === letter);
|
|
81
|
+
return found ?? makeAxisParameter(info, letter);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Collect the canonical parameter letters present at the top level of a code's argument segment, in order. Letters
|
|
85
|
+
* inside "..." strings or {...} expressions are skipped because they belong to a value, not to a parameter - e.g.
|
|
86
|
+
* the `g` in `M98 P"check-filament.g"` and any argument passed on to a macro must not be mistaken for parameters.
|
|
87
|
+
* A leading apostrophe is honoured so a lower-case axis (`'a`) stays distinct from the upper-case letter.
|
|
88
|
+
*/
|
|
89
|
+
function scanParameterLetters(segment) {
|
|
90
|
+
const letters = [];
|
|
91
|
+
let inString = false;
|
|
92
|
+
let braceDepth = 0;
|
|
93
|
+
for (let i = 0; i < segment.length; i++) {
|
|
94
|
+
const ch = segment[i];
|
|
95
|
+
if (inString) {
|
|
96
|
+
if (ch === "\"") {
|
|
97
|
+
inString = false;
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (ch === "\"") {
|
|
102
|
+
inString = true;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (ch === "{") {
|
|
106
|
+
braceDepth++;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (ch === "}") {
|
|
110
|
+
if (braceDepth > 0) {
|
|
111
|
+
braceDepth--;
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (braceDepth > 0) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (ch === ";") {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
if (/[A-Za-z]/.test(ch)) {
|
|
122
|
+
const prev = i > 0 ? segment[i - 1] : "";
|
|
123
|
+
const next = i + 1 < segment.length ? segment[i + 1] : "";
|
|
124
|
+
if (!/[A-Za-z]/.test(prev) && !/[A-Za-z]/.test(next)) {
|
|
125
|
+
letters.push(canonicalAxisLetter(prev === "'" ? prev + ch : ch));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return letters;
|
|
130
|
+
}
|
|
10
131
|
/**
|
|
11
132
|
* Find the enclosing function call (if any) for the cursor position. Walks back from the end of `beforeCursor`
|
|
12
133
|
* keeping track of paren depth so that `max(a, min(b,|` correctly reports `min` with argIndex 1, not `max`.
|
|
@@ -481,7 +602,8 @@ function findParameterAtCursor(line, codeEndColZero, cursorColOne) {
|
|
|
481
602
|
if (!/[A-Za-z]/.test(prev) && !/[A-Za-z]/.test(next)) {
|
|
482
603
|
const valRange = findParameterValueRange(line, i);
|
|
483
604
|
if (cursorColOne >= valRange.startCol && cursorColOne < valRange.endCol) {
|
|
484
|
-
|
|
605
|
+
// A leading apostrophe escapes a lower-case axis (`'a`); a bare letter is folded to upper case
|
|
606
|
+
return { letterColZero: i, letter: canonicalAxisLetter(prev === "'" ? prev + ch : ch) };
|
|
485
607
|
}
|
|
486
608
|
i = valRange.endCol - 1;
|
|
487
609
|
continue;
|
|
@@ -602,7 +724,8 @@ function buildParameterDoc(info, p) {
|
|
|
602
724
|
if (p.values && p.values.length > 0) {
|
|
603
725
|
md += "\n\n**Values:**";
|
|
604
726
|
for (const v of p.values) {
|
|
605
|
-
|
|
727
|
+
const literals = (Array.isArray(v.value) ? v.value : [v.value]).map((x) => `\`${x}\``).join(" / ");
|
|
728
|
+
md += `\n- ${literals} - ${v.description}`;
|
|
606
729
|
}
|
|
607
730
|
}
|
|
608
731
|
const deprecationNote = p.deprecated ?? info.deprecated;
|
|
@@ -629,18 +752,23 @@ function installSuggestWidgetWidth() {
|
|
|
629
752
|
// Also relax Monaco's built-in max-height on the parameter-hints widget so our summary doc (which lists
|
|
630
753
|
// all parameters when no parameter is active) can grow vertically instead of getting an internal scrollbar
|
|
631
754
|
style.textContent = [
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
//
|
|
755
|
+
// suggest, parameter-hints and hover are overflow widgets: a consumer that sets
|
|
756
|
+
// `fixedOverflowWidgets: true` re-parents them to a body-level node outside `.monaco-editor`, so
|
|
757
|
+
// scoping selectors to `.monaco-editor <widget>` silently stops matching. Target the widget classes
|
|
758
|
+
// directly so the rules apply wherever the editor mounts its overflow widgets (and still match the
|
|
759
|
+
// in-editor case when the flag is off)
|
|
760
|
+
".suggest-widget { min-width: min(600px, 90vw); }",
|
|
761
|
+
".suggest-widget .monaco-list { min-width: min(600px, 90vw); }",
|
|
762
|
+
".parameter-hints-widget { max-width: min(800px, 90vw) !important; }",
|
|
763
|
+
".parameter-hints-widget > .phwrapper { max-width: min(800px, 90vw) !important; }",
|
|
764
|
+
// Hover widget: cap width at 800px (90vw on narrow screens so phones don't overflow), and cap height
|
|
637
765
|
// at 50vh so Monaco's positioning math always finds a fit either above or below the cursor. Without
|
|
638
766
|
// a height cap it can compute a height that doesn't fit above when hovering near the top of the file,
|
|
639
767
|
// leaving the tooltip clipped at negative Y. Long docs (M106, M950) scroll internally, which is the
|
|
640
768
|
// right trade-off - forcing inner containers to ignore the computed max-height makes the widget grow
|
|
641
769
|
// past its reserved slot and overlap the source line, hiding the cursor
|
|
642
|
-
".monaco-
|
|
643
|
-
".monaco-
|
|
770
|
+
".monaco-hover { max-width: min(800px, 90vw) !important; max-height: 50vh !important; }",
|
|
771
|
+
".monaco-hover .hover-contents { overflow-wrap: break-word; }",
|
|
644
772
|
// Pin every box in the hover to the same integer pixel height (19 px - matches Monaco's intended
|
|
645
773
|
// `1.35714 * 14` line-height, just rounded). Monaco's default ratio resolves to 18.99996 px at 14 px
|
|
646
774
|
// font; inline phrasing elements (<code>, <strong>, ...) have their own intrinsic metrics that
|
|
@@ -650,16 +778,24 @@ function installSuggestWidgetWidth() {
|
|
|
650
778
|
// inline blocks + an explicit <li> height kills the rounding mismatch at every level. Also mirror
|
|
651
779
|
// the implicit left gutter (from the <ul> bullet indent) on the right so text doesn't butt up against
|
|
652
780
|
// the tooltip edge
|
|
653
|
-
".monaco-
|
|
781
|
+
".monaco-hover .monaco-hover-content { line-height: 19px !important; box-sizing: border-box; overflow-x: hidden; }",
|
|
654
782
|
// Add right-side padding only when the hover renders more than a single line (multi-paragraph
|
|
655
783
|
// content, or a bullet/numbered list). Single-line hovers (just one `<p>`) don't need it and the
|
|
656
784
|
// extra gutter would look off-balance against the natural left margin
|
|
657
|
-
".monaco-
|
|
785
|
+
".monaco-hover .monaco-hover-content:has(p + p, ul, ol) { padding-right: 12px; }",
|
|
658
786
|
// Pin <li> to a clean 19 px integer floor (matches the parent's pinned line-height). Browser default
|
|
659
787
|
// padding/margin gives a fractional ~17.4167 px row that, in lists with many items, accumulates into a
|
|
660
788
|
// half-pixel overflow at the bottom and brings back the phantom scrollbar. min-height (not height) so
|
|
661
789
|
// long parameter descriptions that wrap can grow vertically instead of overlapping the next item
|
|
662
|
-
".monaco-
|
|
790
|
+
".monaco-hover li { min-height: 19px; box-sizing: border-box; }",
|
|
791
|
+
// Re-assert Monaco's own paragraph/list spacing (it ships margin: 8px 0 on these, with the first/last
|
|
792
|
+
// child gaps trimmed and a 20 px bullet indent). Host CSS resets - e.g. Vuetify 4's global reset, which
|
|
793
|
+
// zeroes <p>/<ul> margins - cascade into this body-level hover widget and collapse the gaps between the
|
|
794
|
+
// path, summary and value list. !important keeps the rhythm regardless of the embedding app's reset
|
|
795
|
+
".monaco-hover p, .monaco-hover ul { margin: 8px 0 !important; }",
|
|
796
|
+
".monaco-hover p:first-child, .monaco-hover ul:first-child { margin-top: 0 !important; }",
|
|
797
|
+
".monaco-hover p:last-child, .monaco-hover ul:last-child { margin-bottom: 0 !important; }",
|
|
798
|
+
".monaco-hover ul { padding-left: 20px !important; }"
|
|
663
799
|
].join(" ");
|
|
664
800
|
document.head.appendChild(style);
|
|
665
801
|
suggestWidgetStyleInstalled = true;
|
|
@@ -862,21 +998,16 @@ export function registerProvidersFor(monacoInstance, languageId) {
|
|
|
862
998
|
// just typed it, Monaco should show nothing (so the widget auto-closes) rather than list
|
|
863
999
|
// the very letter that was just typed as the only match
|
|
864
1000
|
const fullTail = lineContent.substring(code.startColumn - 1 + code.code.length);
|
|
865
|
-
const used = new Set();
|
|
866
|
-
const re = /(?<![A-Za-z])[A-Za-z](?![A-Za-z])/g;
|
|
867
|
-
let m;
|
|
868
|
-
while ((m = re.exec(fullTail)) !== null) {
|
|
869
|
-
used.add(m[0].toUpperCase());
|
|
870
|
-
}
|
|
1001
|
+
const used = new Set(scanParameterLetters(fullTail));
|
|
871
1002
|
const triggerHints = { id: "editor.action.triggerParameterHints", title: "Trigger Parameter Hints" };
|
|
872
1003
|
const suggestions = info.parameters
|
|
873
|
-
.filter(p => !used.has(p.letter
|
|
1004
|
+
.filter(p => !used.has(p.letter))
|
|
874
1005
|
.map(p => ({
|
|
875
|
-
label: { label: p.letter, description: p.description },
|
|
1006
|
+
label: { label: gcodeAxisToken(p.letter), description: p.description },
|
|
876
1007
|
kind: monacoInstance.languages.CompletionItemKind.Property,
|
|
877
1008
|
detail: p.description,
|
|
878
1009
|
documentation: md(buildParameterDoc(info, p)),
|
|
879
|
-
insertText: p.letter,
|
|
1010
|
+
insertText: gcodeAxisToken(p.letter),
|
|
880
1011
|
range,
|
|
881
1012
|
// Re-open the signature help tooltip after accepting a parameter letter (Monaco otherwise
|
|
882
1013
|
// closes parameter hints when any completion item is accepted without a command)
|
|
@@ -1003,7 +1134,7 @@ export function registerProvidersFor(monacoInstance, languageId) {
|
|
|
1003
1134
|
}
|
|
1004
1135
|
for (const p of info.parameters) {
|
|
1005
1136
|
const start = label.length + 1;
|
|
1006
|
-
label += " " + p.letter;
|
|
1137
|
+
label += " " + gcodeAxisToken(p.letter);
|
|
1007
1138
|
parameters.push({
|
|
1008
1139
|
label: [start, label.length],
|
|
1009
1140
|
documentation: md(buildParameterDoc(info, p))
|
|
@@ -1013,18 +1144,31 @@ export function registerProvidersFor(monacoInstance, languageId) {
|
|
|
1013
1144
|
// for codes with a unprecedentedParameter, sit on slot 0 while the user is typing that value (no parameter letter typed yet)
|
|
1014
1145
|
const tail = tailForDismissal;
|
|
1015
1146
|
let activeParameter = -1;
|
|
1016
|
-
const seen = tail
|
|
1147
|
+
const seen = scanParameterLetters(tail);
|
|
1017
1148
|
const unprecedentedOffset = info.unprecedentedParameter ? 1 : 0;
|
|
1018
|
-
if (seen
|
|
1019
|
-
const last = seen[seen.length - 1]
|
|
1020
|
-
const idx = info.parameters.findIndex(p => p.letter
|
|
1149
|
+
if (seen.length > 0) {
|
|
1150
|
+
const last = seen[seen.length - 1];
|
|
1151
|
+
const idx = info.parameters.findIndex(p => p.letter === last);
|
|
1021
1152
|
if (idx >= 0) {
|
|
1022
1153
|
activeParameter = idx + unprecedentedOffset;
|
|
1023
1154
|
}
|
|
1024
1155
|
else {
|
|
1025
|
-
//
|
|
1026
|
-
//
|
|
1027
|
-
|
|
1156
|
+
// Letter isn't one of the listed parameters. If it is a supported axis on an axis code (e.g. a
|
|
1157
|
+
// U axis the connected machine isn't configured with) append it so its documentation still shows;
|
|
1158
|
+
// otherwise hide the popup rather than falling back to the misleading generic summary view
|
|
1159
|
+
const axisParam = makeAxisParameter(info, last);
|
|
1160
|
+
if (axisParam) {
|
|
1161
|
+
const start = label.length + 1;
|
|
1162
|
+
label += " " + gcodeAxisToken(axisParam.letter);
|
|
1163
|
+
parameters.push({
|
|
1164
|
+
label: [start, label.length],
|
|
1165
|
+
documentation: md(buildParameterDoc(info, axisParam))
|
|
1166
|
+
});
|
|
1167
|
+
activeParameter = parameters.length - 1;
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
return null;
|
|
1171
|
+
}
|
|
1028
1172
|
}
|
|
1029
1173
|
}
|
|
1030
1174
|
else if (info.unprecedentedParameter) {
|
|
@@ -1096,7 +1240,7 @@ export function registerProvidersFor(monacoInstance, languageId) {
|
|
|
1096
1240
|
const info = findGcode(enclosingCode.code);
|
|
1097
1241
|
const paramAtCursor = info ? findParameterAtCursor(lineContent, enclosingCode.startColumn + enclosingCode.code.length - 1, position.column) : null;
|
|
1098
1242
|
if (info && paramAtCursor) {
|
|
1099
|
-
const param = info
|
|
1243
|
+
const param = lookupParameter(info, paramAtCursor.letter);
|
|
1100
1244
|
if (param) {
|
|
1101
1245
|
const valueRange = findParameterValueRange(lineContent, paramAtCursor.letterColZero);
|
|
1102
1246
|
return {
|
|
@@ -1147,7 +1291,7 @@ export function registerProvidersFor(monacoInstance, languageId) {
|
|
|
1147
1291
|
// inside `1:2:3` of `E1:2:3`). This covers hovers that don't land on the letter itself
|
|
1148
1292
|
const paramAtCursor = findParameterAtCursor(lineContent, enclosing.startColumn + enclosing.code.length - 1, position.column);
|
|
1149
1293
|
if (info && paramAtCursor) {
|
|
1150
|
-
const param = info
|
|
1294
|
+
const param = lookupParameter(info, paramAtCursor.letter);
|
|
1151
1295
|
if (param) {
|
|
1152
1296
|
const valueRange = findParameterValueRange(lineContent, paramAtCursor.letterColZero);
|
|
1153
1297
|
return {
|
|
@@ -1159,7 +1303,9 @@ export function registerProvidersFor(monacoInstance, languageId) {
|
|
|
1159
1303
|
// Fallback: hovered word starts with a parameter letter (e.g. bare `X` in `M84 X Y Z`, where
|
|
1160
1304
|
// the letter has no value after it so findParameterAtCursor may stop before reaching it)
|
|
1161
1305
|
if (info && /^[A-Za-z]/.test(word.word)) {
|
|
1162
|
-
|
|
1306
|
+
// A leading apostrophe just before the word marks a lower-case axis (`'a`)
|
|
1307
|
+
const escaped = word.startColumn >= 2 && lineContent[word.startColumn - 2] === "'";
|
|
1308
|
+
const param = lookupParameter(info, canonicalAxisLetter(escaped ? "'" + firstLetter : firstLetter));
|
|
1163
1309
|
if (param) {
|
|
1164
1310
|
const valueRange = findParameterValueRange(lineContent, word.startColumn - 1);
|
|
1165
1311
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duet3d/monacotokens",
|
|
3
|
-
"version": "3.7.0-
|
|
3
|
+
"version": "3.7.0-beta.2",
|
|
4
4
|
"description": "TypeScript library that holds syntax highlighting files for the Monaco editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,14 +25,21 @@
|
|
|
25
25
|
"url": "https://github.com/Duet3D/MonacoTokens/issues"
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/Duet3D/MonacoTokens#readme",
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@duet3d/objectmodel": "~3.7.0-beta.2",
|
|
30
|
+
"monaco-editor-core": "^0.56.0",
|
|
31
|
+
"monaco-editor": "^0.56.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"monaco-editor-core": { "optional": true },
|
|
35
|
+
"monaco-editor": { "optional": true }
|
|
36
|
+
},
|
|
28
37
|
"devDependencies": {
|
|
29
|
-
"
|
|
38
|
+
"@duet3d/objectmodel": "~3.7.0-beta.2",
|
|
39
|
+
"monaco-editor-core": "^0.56.0",
|
|
40
|
+
"typescript": "^7.0.2"
|
|
30
41
|
},
|
|
31
42
|
"files": [
|
|
32
43
|
"/dist"
|
|
33
|
-
]
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"@duet3d/objectmodel": "~3.7.0-alpha.8",
|
|
36
|
-
"monaco-editor-core": "^0.55.1"
|
|
37
|
-
}
|
|
44
|
+
]
|
|
38
45
|
}
|