typeglish 0.1.0
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/LICENSE +201 -0
- package/NOTICE +13 -0
- package/README.md +301 -0
- package/dist/chunk-3NQ3VYO6.js +566 -0
- package/dist/chunk-3NQ3VYO6.js.map +1 -0
- package/dist/chunk-KRYC4OTL.js +3573 -0
- package/dist/chunk-KRYC4OTL.js.map +1 -0
- package/dist/chunk-NLQIL5WD.js +25 -0
- package/dist/chunk-NLQIL5WD.js.map +1 -0
- package/dist/chunk-PR4QN5HX.js +39 -0
- package/dist/chunk-PR4QN5HX.js.map +1 -0
- package/dist/chunk-PXAMTGYY.js +3 -0
- package/dist/chunk-PXAMTGYY.js.map +1 -0
- package/dist/chunk-VMACILYN.js +140 -0
- package/dist/chunk-VMACILYN.js.map +1 -0
- package/dist/chunk-Y64EAEYB.js +10919 -0
- package/dist/chunk-Y64EAEYB.js.map +1 -0
- package/dist/chunk-YH4ZLQ4G.js +4260 -0
- package/dist/chunk-YH4ZLQ4G.js.map +1 -0
- package/dist/chunk-ZKMHZHID.js +56 -0
- package/dist/chunk-ZKMHZHID.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +447 -0
- package/dist/cli.js.map +1 -0
- package/dist/compile-DViQS2oG.d.ts +310 -0
- package/dist/diagnostics-BWJ_oMd8.d.ts +49 -0
- package/dist/exhaustiveness-VBVWCX5W.js +123 -0
- package/dist/exhaustiveness-VBVWCX5W.js.map +1 -0
- package/dist/index-Df_QnTgq.d.ts +313 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +11 -0
- package/dist/mcp.js +36378 -0
- package/dist/mcp.js.map +1 -0
- package/dist/monaco.css +46 -0
- package/dist/monaco.d.ts +43 -0
- package/dist/monaco.js +971 -0
- package/dist/monaco.js.map +1 -0
- package/dist/node.d.ts +21 -0
- package/dist/node.js +8 -0
- package/dist/node.js.map +1 -0
- package/dist/spell-node.d.ts +8 -0
- package/dist/spell-node.js +23 -0
- package/dist/spell-node.js.map +1 -0
- package/dist/spell.d.ts +18 -0
- package/dist/spell.js +4 -0
- package/dist/spell.js.map +1 -0
- package/dist/z3-MWU3GSVT.js +5 -0
- package/dist/z3-MWU3GSVT.js.map +1 -0
- package/package.json +160 -0
package/dist/monaco.js
ADDED
|
@@ -0,0 +1,971 @@
|
|
|
1
|
+
import { OBLIGATE, PROHIBIT, compile, layoutColumns, INDENT_UNIT } from './chunk-KRYC4OTL.js';
|
|
2
|
+
export { compile } from './chunk-KRYC4OTL.js';
|
|
3
|
+
import { MANY_CLASS, NON_ACTION_LEADS, structure, COMPLETION_TRIGGERS, renumberSubSteps, BUILTIN_CONFIG_SCHEMA } from './chunk-YH4ZLQ4G.js';
|
|
4
|
+
import { keywordRegex, MODAL_KEYWORDS, CONTROL_KEYWORDS, BRANCH_KEYWORDS, DEFINITION_KEYWORDS, OPERATOR_KEYWORDS, copulaConnector, keywordAltCI, QUANTIFIER_KEYWORDS, QUANTIFIER_WORDS, NUMBER_WORDS, CARDINAL_QUANTITIES, COMPARISON_SYMBOLS, EXACTNESS_MARKERS, copulaAlternation, FREQUENCY_KEYWORDS, keywordAlt, COMPARISON_KEYWORDS, COMMAND_WORDS, TAG_NAME_SRC, isProseBlock, lineOrdinals } from './chunk-Y64EAEYB.js';
|
|
5
|
+
import './chunk-PR4QN5HX.js';
|
|
6
|
+
|
|
7
|
+
// src/core/semantic-tokens.ts
|
|
8
|
+
var SEM_TOKEN_TYPES = [
|
|
9
|
+
"directive",
|
|
10
|
+
// 0 $IMPORT/$CONFIG/$REQUIRE/$SWITCH … — import red
|
|
11
|
+
"keyword",
|
|
12
|
+
// 1 IF/WHEN/THEN — control purple
|
|
13
|
+
"modal",
|
|
14
|
+
// 2 MUST/NEVER/NOT — red
|
|
15
|
+
"operator",
|
|
16
|
+
// 3 IS/ARE/AND/OR/INCLUDING — cyan
|
|
17
|
+
"variable",
|
|
18
|
+
// 4 @{x} / $x — insertion blue
|
|
19
|
+
"section",
|
|
20
|
+
// 5 <tag> / @<x> — section blue
|
|
21
|
+
"tool",
|
|
22
|
+
// 6 @[x] — insertion blue
|
|
23
|
+
"comment",
|
|
24
|
+
// 7 // free comment — muted green
|
|
25
|
+
"string",
|
|
26
|
+
// 8 "…" literal + fenced code — orange
|
|
27
|
+
"heading",
|
|
28
|
+
// 9 # heading — section blue
|
|
29
|
+
"declaration",
|
|
30
|
+
// 10 a declared subject/owner/name/value — gold
|
|
31
|
+
"property",
|
|
32
|
+
// 11 a possessed property noun — muted gold
|
|
33
|
+
"annotation",
|
|
34
|
+
// 12 RETIRED from emission (kept for index stability) — @@ notes now emit 'comment': an annotation IS an attached comment
|
|
35
|
+
"list",
|
|
36
|
+
// 13 - * 1. list markers — grey
|
|
37
|
+
"link",
|
|
38
|
+
// 14 [text](href) — blue
|
|
39
|
+
"strong",
|
|
40
|
+
// 15 **bold**
|
|
41
|
+
"emphasis",
|
|
42
|
+
// 16 *italic*
|
|
43
|
+
"strike",
|
|
44
|
+
// 17 ~~struck~~
|
|
45
|
+
"code",
|
|
46
|
+
// 18 `inline code`
|
|
47
|
+
"markup",
|
|
48
|
+
// 19 the **, *, ~~, ` markers themselves — dim
|
|
49
|
+
"quantifier",
|
|
50
|
+
// 20 ALL/SOME/NO/ONLY/MOST + cardinal quantities (only one, many) — quantity green
|
|
51
|
+
"scaffold",
|
|
52
|
+
// 21 <$name> tags — control-plane, hidden from the MODEL (strip whole extent) — directive red
|
|
53
|
+
"literal",
|
|
54
|
+
// 22 <"name"> tags — literal zone, hidden from the CHECKER (verbatim body) — string orange
|
|
55
|
+
"defterm",
|
|
56
|
+
// 23 a definition entry's `term::` — the reference-table label, gold
|
|
57
|
+
"defbody",
|
|
58
|
+
// 24 a definition entry's body/continuations — one neutral colour (rendered, not authored)
|
|
59
|
+
"toolkey",
|
|
60
|
+
// 25 a $TOOL/$SERVICE body field key + its `::` — a record key, blue
|
|
61
|
+
"toolval"
|
|
62
|
+
// 26 a $TOOL/$SERVICE body value / free body line — one neutral colour, like defbody
|
|
63
|
+
];
|
|
64
|
+
var TOKEN_CLASSIFICATION = {
|
|
65
|
+
directive: { base: "macro", modifier: "directive" },
|
|
66
|
+
// $-commands read as preprocessor abroad
|
|
67
|
+
keyword: { base: "keyword", modifier: null },
|
|
68
|
+
modal: { base: "keyword", modifier: "modal" },
|
|
69
|
+
operator: { base: "operator", modifier: null },
|
|
70
|
+
variable: { base: "variable", modifier: "interpolation" },
|
|
71
|
+
section: { base: "namespace", modifier: "section" },
|
|
72
|
+
// a section is a named container
|
|
73
|
+
tool: { base: "function", modifier: "tool" },
|
|
74
|
+
// a tool is callable
|
|
75
|
+
comment: { base: "comment", modifier: null },
|
|
76
|
+
string: { base: "string", modifier: null },
|
|
77
|
+
heading: { base: "namespace", modifier: "heading" },
|
|
78
|
+
declaration: { base: "variable", modifier: "declaration" },
|
|
79
|
+
// 'declaration' is a STANDARD modifier
|
|
80
|
+
property: { base: "property", modifier: null },
|
|
81
|
+
annotation: { base: "comment", modifier: "annotation" },
|
|
82
|
+
// retired from emission
|
|
83
|
+
list: { base: "list", modifier: null },
|
|
84
|
+
// graceful silence — plain markers abroad
|
|
85
|
+
link: { base: "link", modifier: null },
|
|
86
|
+
// graceful silence
|
|
87
|
+
strong: { base: "strong", modifier: null },
|
|
88
|
+
// graceful silence (style-only)
|
|
89
|
+
emphasis: { base: "emphasis", modifier: null },
|
|
90
|
+
strike: { base: "strike", modifier: null },
|
|
91
|
+
code: { base: "code", modifier: null },
|
|
92
|
+
markup: { base: "markup", modifier: null },
|
|
93
|
+
quantifier: { base: "keyword", modifier: "quantifier" },
|
|
94
|
+
scaffold: { base: "macro", modifier: "scaffold" },
|
|
95
|
+
literal: { base: "string", modifier: "literalZone" },
|
|
96
|
+
defterm: { base: "property", modifier: "defterm" },
|
|
97
|
+
defbody: { base: "defbody", modifier: null },
|
|
98
|
+
// graceful silence — a plain body abroad is right
|
|
99
|
+
toolkey: { base: "property", modifier: "toolkey" },
|
|
100
|
+
toolval: { base: "toolval", modifier: null }
|
|
101
|
+
// graceful silence
|
|
102
|
+
};
|
|
103
|
+
var SEM_LEGEND = (() => {
|
|
104
|
+
const tokenTypes = [];
|
|
105
|
+
const tokenModifiers = [];
|
|
106
|
+
for (const t of SEM_TOKEN_TYPES) {
|
|
107
|
+
const c = TOKEN_CLASSIFICATION[t];
|
|
108
|
+
if (!tokenTypes.includes(c.base)) tokenTypes.push(c.base);
|
|
109
|
+
if (c.modifier !== null && !tokenModifiers.includes(c.modifier)) tokenModifiers.push(c.modifier);
|
|
110
|
+
}
|
|
111
|
+
return { tokenTypes, tokenModifiers };
|
|
112
|
+
})();
|
|
113
|
+
keywordRegex(MODAL_KEYWORDS);
|
|
114
|
+
keywordRegex([...CONTROL_KEYWORDS, ...BRANCH_KEYWORDS, ...DEFINITION_KEYWORDS]);
|
|
115
|
+
keywordRegex(OPERATOR_KEYWORDS);
|
|
116
|
+
new RegExp(`(?<!\\b(?:${keywordAltCI(QUANTIFIER_KEYWORDS)})\\s)\\b(?![Tt]here\\b)([A-Za-z][A-Za-z0-9_]*)(${copulaConnector}\\b)`);
|
|
117
|
+
keywordAltCI(QUANTIFIER_WORDS.filter((q) => q.kind !== "exclusive").flatMap((q) => q.words));
|
|
118
|
+
keywordAltCI(QUANTIFIER_WORDS.filter((q) => q.kind === "exclusive").flatMap((q) => q.words));
|
|
119
|
+
var CLAUSE_LEAD = `^\\s*(?:[-*+]\\s+|\\d+[.)]\\s+)?|\\b${keywordAltCI(["THEN"])}\\s+`;
|
|
120
|
+
var NUM_ALT = `(?:${Object.keys(NUMBER_WORDS).map((w) => keywordAltCI([w])).join("|")}|\\d+)`;
|
|
121
|
+
var CARD_FIXED_ALT = [...CARDINAL_QUANTITIES].sort((a, b) => b.phrase.length - a.phrase.length).map((q) => keywordAltCI([q.phrase])).join("|");
|
|
122
|
+
var BOUND_ALT = COMPARISON_SYMBOLS.filter((c) => c.bound !== void 0).map((c) => keywordAltCI([c.bound])).join("|");
|
|
123
|
+
var CARD_QUANTITY = `(?:(?:${keywordAltCI([...EXACTNESS_MARKERS])})\\s+${NUM_ALT}|(?:${BOUND_ALT})\\s+${NUM_ALT}|${CARD_FIXED_ALT}|${NUM_ALT})`;
|
|
124
|
+
[new RegExp(`(\\b${keywordAltCI(["THERE"])}\\s+(?:${keywordAltCI(["IS", "ARE", "WAS", "WERE", "EXISTS", "EXIST"])})\\s+)(${CARD_QUANTITY})(?=\\s+[A-Za-z])`), ["", "quantifier"]];
|
|
125
|
+
var CARD_FIXED_HAVE_ALT = [...CARDINAL_QUANTITIES].filter((q) => q.phrase !== "a" && q.phrase !== "an").sort((a, b) => b.phrase.length - a.phrase.length).map((q) => keywordAltCI([q.phrase])).join("|");
|
|
126
|
+
var CARD_QUANTITY_HAVE = `(?:(?:${keywordAltCI([...EXACTNESS_MARKERS])})\\s+${NUM_ALT}|(?:${BOUND_ALT})\\s+${NUM_ALT}|${CARD_FIXED_HAVE_ALT}|${NUM_ALT})`;
|
|
127
|
+
[new RegExp(`(\\b(?:${keywordAltCI(["HAS", "HAVE"])})\\s+)(${CARD_QUANTITY_HAVE})(?=\\s+[A-Za-z])`), ["", "quantifier"]];
|
|
128
|
+
var SUBJ_ABSTAIN_1 = `(?<!\\b(?:${keywordAltCI(QUANTIFIER_KEYWORDS)}|${NUM_ALT})\\s)`;
|
|
129
|
+
var SUBJ_ABSTAIN_2 = `(?<!\\b(?:${keywordAltCI(QUANTIFIER_KEYWORDS)}|${NUM_ALT})\\s[A-Za-z][A-Za-z0-9_]*\\s)`;
|
|
130
|
+
var MANY_ALT = MANY_CLASS.map((w) => keywordAltCI([w])).join("|");
|
|
131
|
+
var NUM_GE2_ALT = `(?:${Object.entries(NUMBER_WORDS).filter(([, n]) => n >= 2).map(([w]) => keywordAltCI([w])).join("|")}|[2-9]|[1-9]\\d+)`;
|
|
132
|
+
[new RegExp(`${SUBJ_ABSTAIN_1}${SUBJ_ABSTAIN_2}\\b((?:${copulaAlternation})\\s+)(${keywordAltCI(["THE"])}\\s+${keywordAltCI(["ONLY"])})(?=\\s+[A-Za-z])`), ["", "quantifier"]];
|
|
133
|
+
[new RegExp(`${SUBJ_ABSTAIN_1}${SUBJ_ABSTAIN_2}\\b((?:${copulaAlternation})\\s+)(${keywordAltCI(["ONE"])}\\s+${keywordAltCI(["OF"])}\\s+(?:${keywordAltCI(["THE"])}\\s+)?(?:${MANY_ALT}|${NUM_GE2_ALT}))(?=\\s+[A-Za-z])`), ["", "quantifier"]];
|
|
134
|
+
var ACTION_VERB_EXCL = [
|
|
135
|
+
...NON_ACTION_LEADS,
|
|
136
|
+
...QUANTIFIER_KEYWORDS.map((w) => w.toLowerCase()),
|
|
137
|
+
...Object.keys(NUMBER_WORDS),
|
|
138
|
+
...EXACTNESS_MARKERS.map((w) => w.toLowerCase()),
|
|
139
|
+
...CARDINAL_QUANTITIES.map((q) => q.phrase.split(" ")[0] ?? ""),
|
|
140
|
+
...[...OBLIGATE, ...PROHIBIT].map((w) => (w.split(" ")[0] ?? "").toLowerCase())
|
|
141
|
+
].filter((w) => w !== "");
|
|
142
|
+
var ACTION_VERB = `(?!(?:${[...new Set(ACTION_VERB_EXCL)].map((w) => keywordAltCI([w])).join("|")})\\b)[A-Za-z][A-Za-z-]*`;
|
|
143
|
+
var OBLIGATE_ALT = OBLIGATE.map((w) => keywordAltCI([w])).join("|");
|
|
144
|
+
[new RegExp(`((?:${OBLIGATE_ALT})\\s+(?!${keywordAltCI(["NOT"])}\\b)${ACTION_VERB}\\s+)(${CARD_QUANTITY_HAVE})(?=\\s+[A-Za-z])`), ["", "quantifier"]];
|
|
145
|
+
var FREQ_COLOR_ALT = FREQUENCY_KEYWORDS.map((w) => keywordAltCI([w])).join("|");
|
|
146
|
+
[new RegExp(`((?:${CLAUSE_LEAD})(?:[Yy]ou\\s+)?|(?:${keywordAlt(MODAL_KEYWORDS)})\\s+(?:${keywordAltCI(["NOT"])}\\s+)?)(${FREQ_COLOR_ALT})\\b(?=,?\\s+[A-Za-z])`), ["", "quantifier"]];
|
|
147
|
+
new RegExp(`\\b((?!(?:${keywordAltCI([...BRANCH_KEYWORDS, ...CONTROL_KEYWORDS])})\\b)[A-Za-z][A-Za-z0-9_]*)(\\s+(?:${keywordAlt(MODAL_KEYWORDS)})\\b)`);
|
|
148
|
+
[new RegExp(`(@\\{[A-Za-z0-9_.]+\\})(\\s+)(${keywordAlt(COMPARISON_KEYWORDS)})\\b`), ["variable", "", "operator"]];
|
|
149
|
+
new Map(SEM_LEGEND.tokenTypes.map((t, i) => [t, i]));
|
|
150
|
+
new Map(SEM_LEGEND.tokenModifiers.map((m, i) => [m, 1 << i]));
|
|
151
|
+
|
|
152
|
+
// src/core/editor-config.ts
|
|
153
|
+
var COMMENT_SYNTAX = { line: "//", block: ["/*", "*/"] };
|
|
154
|
+
var COLORIZED_BRACKET_PAIRS = [];
|
|
155
|
+
var OPEN_TAG_LINE_SRC = `^\\s*<${TAG_NAME_SRC}(\\s[^>]*)?>\\s*(\\/\\/.*)?$`;
|
|
156
|
+
var CLOSE_TAG_LINE_SRC = `^\\s*<\\/${TAG_NAME_SRC}>\\s*(\\/\\/.*)?$`;
|
|
157
|
+
var AUTO_CLOSE_TAG_SRC = `^\\s*<(${TAG_NAME_SRC})>$`;
|
|
158
|
+
var INDENT_OPENERS_SRC = `^\\s*(?:<${TAG_NAME_SRC}(?:\\s[^>]*)?>|\\$(?:ELSE\\s+)?IF\\s+[^:]*:|\\$ELSE\\s*:|\\$(?:TOOL|SERVICE)\\s+[a-z][a-z0-9_]*|\\$TEST\\b[^:]*:\\s*"[^"]*")\\s*(\\/\\/.*)?$`;
|
|
159
|
+
var TOKEN_PALETTE = {
|
|
160
|
+
directive: { fg: "ff7b72", bold: true },
|
|
161
|
+
// $IMPORT/$CONFIG/$REQUIRE — import red
|
|
162
|
+
keyword: { fg: "c586c0", bold: true },
|
|
163
|
+
// IF/WHEN/THEN — control purple
|
|
164
|
+
modal: { fg: "ff7b72", bold: true },
|
|
165
|
+
// MUST/NEVER/NOT — red
|
|
166
|
+
operator: { fg: "56b6c2" },
|
|
167
|
+
// IS/ARE/AND/OR/INCLUDING — cyan
|
|
168
|
+
variable: { fg: "79c0ff" },
|
|
169
|
+
// @{x} / $x — insertion blue
|
|
170
|
+
section: { fg: "58a6ff", bold: true },
|
|
171
|
+
// <tag> / @<x> — section blue
|
|
172
|
+
tool: { fg: "79c0ff" },
|
|
173
|
+
// @[x] — insertion blue
|
|
174
|
+
comment: { fg: "6b8f71", italic: true },
|
|
175
|
+
// // comment AND @@ annotation — muted green
|
|
176
|
+
string: { fg: "ffa657" },
|
|
177
|
+
// "…" literal + fenced code — orange
|
|
178
|
+
heading: { fg: "58a6ff", bold: true },
|
|
179
|
+
// # heading — section blue
|
|
180
|
+
declaration: { fg: "e3b341", bold: true },
|
|
181
|
+
// a declared subject/owner/name/value — gold
|
|
182
|
+
property: { fg: "c9a86a" },
|
|
183
|
+
// a possessed property noun — muted gold
|
|
184
|
+
annotation: { fg: "6b8f71", italic: true },
|
|
185
|
+
// RETIRED from emission — kept for legend index stability
|
|
186
|
+
list: { fg: "8b949e" },
|
|
187
|
+
// - * 1. list markers — grey
|
|
188
|
+
link: { fg: "4493f8" },
|
|
189
|
+
// [text](href)
|
|
190
|
+
strong: { bold: true },
|
|
191
|
+
// **bold** — content bold, colour kept
|
|
192
|
+
emphasis: { italic: true },
|
|
193
|
+
// *italic*
|
|
194
|
+
strike: { fg: "8b949e", strikethrough: true },
|
|
195
|
+
// ~~struck~~
|
|
196
|
+
code: { fg: "79c0ff" },
|
|
197
|
+
// `inline code`
|
|
198
|
+
markup: { fg: "6e7681" },
|
|
199
|
+
// the **, *, ~~, ` markers — dim so content pops
|
|
200
|
+
quantifier: { fg: "7ee787", bold: true },
|
|
201
|
+
// ALL/SOME/NO/ONLY/MOST — quantity green
|
|
202
|
+
scaffold: { fg: "ff7b72", bold: true },
|
|
203
|
+
// <$name> tags — control-plane, $-family red
|
|
204
|
+
literal: { fg: "ffa657", bold: true },
|
|
205
|
+
// <"name"> tags — literal zone, quote-family orange
|
|
206
|
+
defterm: { fg: "e3b341", bold: true },
|
|
207
|
+
// a definition `term::` — the reference-table label, gold
|
|
208
|
+
defbody: { fg: "a8b3bd" },
|
|
209
|
+
// a definition body — one neutral colour
|
|
210
|
+
toolkey: { fg: "79c0ff", bold: true },
|
|
211
|
+
// a $TOOL/$SERVICE field key — a record key, blue
|
|
212
|
+
toolval: { fg: "a8b3bd" }
|
|
213
|
+
// a $TOOL/$SERVICE value — neutral, like defbody
|
|
214
|
+
};
|
|
215
|
+
var EDITOR_CHROME = {
|
|
216
|
+
"editor.background": "#0d1117",
|
|
217
|
+
"editor.foreground": "#e6edf3",
|
|
218
|
+
"editorLineNumber.foreground": "#6e7681",
|
|
219
|
+
"editorLineNumber.activeForeground": "#e6edf3",
|
|
220
|
+
"editor.lineHighlightBackground": "#161b22",
|
|
221
|
+
"editorGutter.background": "#0d1117",
|
|
222
|
+
"editorIndentGuide.background1": "#21262d",
|
|
223
|
+
"editorWidget.background": "#161b22",
|
|
224
|
+
"editorWidget.border": "#2a3340",
|
|
225
|
+
"editorHoverWidget.background": "#161b22",
|
|
226
|
+
"editorHoverWidget.border": "#2a3340",
|
|
227
|
+
// Suggest (autocomplete) dropdown. Without these it inherits a background ~9 RGB points off the
|
|
228
|
+
// editor's (#161b22 vs #0d1117) with no border or shadow, so it blends into the text and reads as
|
|
229
|
+
// "cut off." Give it an elevated surface, a clear border, an accent-filled selection, and a real
|
|
230
|
+
// drop shadow so it reads as a floating box.
|
|
231
|
+
"editorSuggestWidget.background": "#1c2330",
|
|
232
|
+
"editorSuggestWidget.border": "#3d4a5c",
|
|
233
|
+
"editorSuggestWidget.foreground": "#e6edf3",
|
|
234
|
+
"editorSuggestWidget.selectedBackground": "#1f6feb",
|
|
235
|
+
"editorSuggestWidget.selectedForeground": "#ffffff",
|
|
236
|
+
"editorSuggestWidget.highlightForeground": "#79c0ff",
|
|
237
|
+
"editorSuggestWidget.focusHighlightForeground": "#cae8ff",
|
|
238
|
+
"widget.shadow": "#00000088"
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// src/web/blocks.ts
|
|
242
|
+
var BLOCK_TYPES = {
|
|
243
|
+
role: {
|
|
244
|
+
label: "Role",
|
|
245
|
+
does: "Sets the agent's persona and purpose.",
|
|
246
|
+
doesNot: "Hold rules or steps \u2014 put those in Constraints or Process.",
|
|
247
|
+
bodyShape: "prose",
|
|
248
|
+
unit: "line",
|
|
249
|
+
canHaveChildren: false,
|
|
250
|
+
accent: "var(--v-role)"
|
|
251
|
+
},
|
|
252
|
+
constraints: {
|
|
253
|
+
label: "Constraints",
|
|
254
|
+
does: "Testable rules the model must follow \u2014 one per line.",
|
|
255
|
+
doesNot: "Describe persona or sequence.",
|
|
256
|
+
bodyShape: "rules",
|
|
257
|
+
unit: "rule",
|
|
258
|
+
canHaveChildren: true,
|
|
259
|
+
accent: "var(--v-prohibition)"
|
|
260
|
+
},
|
|
261
|
+
process: {
|
|
262
|
+
label: "Process",
|
|
263
|
+
does: "Ordered steps the agent works through.",
|
|
264
|
+
doesNot: "Hold standalone rules.",
|
|
265
|
+
bodyShape: "steps",
|
|
266
|
+
unit: "step",
|
|
267
|
+
canHaveChildren: true,
|
|
268
|
+
accent: "var(--v-conditional)"
|
|
269
|
+
},
|
|
270
|
+
examples: {
|
|
271
|
+
label: "Examples",
|
|
272
|
+
does: "Few-shot examples that show the desired behaviour.",
|
|
273
|
+
doesNot: "State rules \u2014 show, don't tell.",
|
|
274
|
+
bodyShape: "pairs",
|
|
275
|
+
unit: "line",
|
|
276
|
+
canHaveChildren: true,
|
|
277
|
+
accent: "var(--v-example)"
|
|
278
|
+
},
|
|
279
|
+
output: {
|
|
280
|
+
label: "Output",
|
|
281
|
+
does: "The required output format or shape.",
|
|
282
|
+
doesNot: "Hold persona or rules.",
|
|
283
|
+
bodyShape: "spec",
|
|
284
|
+
unit: "line",
|
|
285
|
+
canHaveChildren: false,
|
|
286
|
+
accent: "var(--v-format)"
|
|
287
|
+
},
|
|
288
|
+
tone: {
|
|
289
|
+
label: "Tone",
|
|
290
|
+
does: "Voice and writing style.",
|
|
291
|
+
doesNot: "Hold rules or steps.",
|
|
292
|
+
bodyShape: "prose",
|
|
293
|
+
unit: "line",
|
|
294
|
+
canHaveChildren: false,
|
|
295
|
+
accent: "var(--v-directive)"
|
|
296
|
+
},
|
|
297
|
+
prose: {
|
|
298
|
+
label: "Prose",
|
|
299
|
+
does: "Free-form text \u2014 the escape hatch.",
|
|
300
|
+
doesNot: "Get analysed or measured structurally.",
|
|
301
|
+
bodyShape: "prose",
|
|
302
|
+
unit: "line",
|
|
303
|
+
canHaveChildren: false,
|
|
304
|
+
accent: "var(--inert)"
|
|
305
|
+
},
|
|
306
|
+
comment: {
|
|
307
|
+
label: "Comment",
|
|
308
|
+
does: "A note for authors.",
|
|
309
|
+
doesNot: "Ever reach the model.",
|
|
310
|
+
bodyShape: "note",
|
|
311
|
+
unit: "line",
|
|
312
|
+
canHaveChildren: false,
|
|
313
|
+
accent: "var(--comment)"
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/core/profile.ts
|
|
318
|
+
function profileHoverMarkdown(info) {
|
|
319
|
+
const rows = [];
|
|
320
|
+
if (info.residual !== void 0)
|
|
321
|
+
;
|
|
322
|
+
if (info.coveredBy)
|
|
323
|
+
;
|
|
324
|
+
if (info.cases) ;
|
|
325
|
+
else if (info.isHardRule)
|
|
326
|
+
;
|
|
327
|
+
if (info.delta)
|
|
328
|
+
;
|
|
329
|
+
if (rows.length === 0) return null;
|
|
330
|
+
return `**statement profile** \u2014 what the compiler has measured about this statement:
|
|
331
|
+
|
|
332
|
+
- ${rows.join("\n- ")}`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// src/web/lsp-client.ts
|
|
336
|
+
var worker = null;
|
|
337
|
+
var nextId = 1;
|
|
338
|
+
var pending = /* @__PURE__ */ new Map();
|
|
339
|
+
var readyCallbacks = /* @__PURE__ */ new Set();
|
|
340
|
+
function getWorker() {
|
|
341
|
+
if (!worker) {
|
|
342
|
+
worker = new Worker(new URL("./lsp-worker.ts", import.meta.url), { type: "module" });
|
|
343
|
+
worker.onmessage = (e) => {
|
|
344
|
+
const msg = e.data;
|
|
345
|
+
if ("notification" in msg) {
|
|
346
|
+
if (msg.notification === "dictionaryReady") for (const cb of readyCallbacks) cb();
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const resolve = pending.get(msg.id);
|
|
350
|
+
if (resolve) {
|
|
351
|
+
pending.delete(msg.id);
|
|
352
|
+
resolve(msg.result);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return worker;
|
|
357
|
+
}
|
|
358
|
+
if (import.meta.hot) {
|
|
359
|
+
import.meta.hot.on("vite:beforeUpdate", () => {
|
|
360
|
+
worker?.terminate();
|
|
361
|
+
worker = null;
|
|
362
|
+
pending.clear();
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
function send(req) {
|
|
366
|
+
const id = nextId++;
|
|
367
|
+
return new Promise((resolve) => {
|
|
368
|
+
pending.set(id, resolve);
|
|
369
|
+
getWorker().postMessage({ ...req, id });
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
function requestCompletion(before, source, line, schema) {
|
|
373
|
+
return send({ kind: "completion", before, source, line, schema });
|
|
374
|
+
}
|
|
375
|
+
function requestHover(source, line, character) {
|
|
376
|
+
return send({ kind: "hover", source, line, character });
|
|
377
|
+
}
|
|
378
|
+
function requestDefinition(source, line, character) {
|
|
379
|
+
return send({ kind: "definition", source, line, character });
|
|
380
|
+
}
|
|
381
|
+
function requestSemanticTokens(source) {
|
|
382
|
+
return send({ kind: "semanticTokens", source });
|
|
383
|
+
}
|
|
384
|
+
function requestDocumentSymbols(source) {
|
|
385
|
+
return send({ kind: "documentSymbol", source });
|
|
386
|
+
}
|
|
387
|
+
function requestFoldingRanges(source) {
|
|
388
|
+
return send({ kind: "foldingRanges", source });
|
|
389
|
+
}
|
|
390
|
+
function requestLinkedEdit(source, line) {
|
|
391
|
+
return send({ kind: "linkedEdit", source, line });
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/web/monaco-setup.ts
|
|
395
|
+
var LANG_ID = "tg";
|
|
396
|
+
var FILE_EXT = ".tg";
|
|
397
|
+
var THEME_ID = "typeglish-dark";
|
|
398
|
+
var profileFn = null;
|
|
399
|
+
var configSchemaCtx = BUILTIN_CONFIG_SCHEMA;
|
|
400
|
+
function profileMarkdown(line0) {
|
|
401
|
+
const info = profileFn?.(line0);
|
|
402
|
+
return info ? profileHoverMarkdown(info) : null;
|
|
403
|
+
}
|
|
404
|
+
function inferBlockType(label) {
|
|
405
|
+
const norm = label.trim().toLowerCase();
|
|
406
|
+
for (const key of Object.keys(BLOCK_TYPES)) {
|
|
407
|
+
if (BLOCK_TYPES[key].label.toLowerCase() === norm) return key;
|
|
408
|
+
}
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
function setupMonaco(monaco) {
|
|
412
|
+
const alreadyRegistered = monaco.languages.getLanguages().some((l) => l.id === LANG_ID);
|
|
413
|
+
if (!alreadyRegistered) {
|
|
414
|
+
monaco.languages.register({ id: LANG_ID, extensions: [FILE_EXT], aliases: ["TypeGlish", "tg"] });
|
|
415
|
+
}
|
|
416
|
+
const OPEN_TAG = new RegExp(OPEN_TAG_LINE_SRC, "i");
|
|
417
|
+
monaco.languages.setLanguageConfiguration(LANG_ID, {
|
|
418
|
+
// `//` and `/* … */` — enables the editor's native toggle-comment on `.tg`.
|
|
419
|
+
comments: { lineComment: COMMENT_SYNTAX.line, blockComment: COMMENT_SYNTAX.block },
|
|
420
|
+
// NO colorized bracket pairs — the switch that actually disables bracket-pair colorization (the
|
|
421
|
+
// editor option is unreliably honoured). Brackets are prose punctuation, pointer-token interiors,
|
|
422
|
+
// or neutral definition-body text — never pairs to rainbow by nesting depth.
|
|
423
|
+
colorizedBracketPairs: COLORIZED_BRACKET_PAIRS,
|
|
424
|
+
indentationRules: {
|
|
425
|
+
increaseIndentPattern: new RegExp(INDENT_OPENERS_SRC, "i"),
|
|
426
|
+
decreaseIndentPattern: new RegExp(CLOSE_TAG_LINE_SRC, "i")
|
|
427
|
+
},
|
|
428
|
+
onEnterRules: [
|
|
429
|
+
{
|
|
430
|
+
beforeText: OPEN_TAG,
|
|
431
|
+
action: { indentAction: monaco.languages.IndentAction.Indent }
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
});
|
|
435
|
+
const theme = {
|
|
436
|
+
base: "vs-dark",
|
|
437
|
+
inherit: true,
|
|
438
|
+
// One rule per token type — DERIVED, never authored here: the classification (semantic-tokens.ts)
|
|
439
|
+
// says how each type travels on the wire (`base.modifier`), the palette (editor-config.ts) says how
|
|
440
|
+
// it paints. This file only assembles the two SSOTs, so the exact palette lands wherever our theme
|
|
441
|
+
// is installed while a foreign editor's theme colours the standard bases natively.
|
|
442
|
+
rules: SEM_TOKEN_TYPES.map((t) => {
|
|
443
|
+
const c = TOKEN_CLASSIFICATION[t];
|
|
444
|
+
const p = TOKEN_PALETTE[t];
|
|
445
|
+
const fontStyle = [p.bold === true ? "bold" : null, p.italic === true ? "italic" : null, p.strikethrough === true ? "strikethrough" : null].filter((s) => s !== null).join(" ");
|
|
446
|
+
return {
|
|
447
|
+
token: c.modifier !== null ? `${c.base}.${c.modifier}` : c.base,
|
|
448
|
+
...p.fg !== void 0 ? { foreground: p.fg } : {},
|
|
449
|
+
...fontStyle !== "" ? { fontStyle } : {}
|
|
450
|
+
};
|
|
451
|
+
}),
|
|
452
|
+
// The chrome (surfaces, gutter, widgets) is editor-config's — the same table a VS Code theme
|
|
453
|
+
// generator would read, so the playground and any generated theme can never disagree.
|
|
454
|
+
colors: EDITOR_CHROME
|
|
455
|
+
};
|
|
456
|
+
monaco.editor.defineTheme(THEME_ID, theme);
|
|
457
|
+
if (alreadyRegistered) return;
|
|
458
|
+
monaco.languages.registerFoldingRangeProvider(LANG_ID, {
|
|
459
|
+
async provideFoldingRanges(model) {
|
|
460
|
+
return await requestFoldingRanges(model.getValue());
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
monaco.languages.registerLinkedEditingRangeProvider(LANG_ID, {
|
|
464
|
+
async provideLinkedEditingRanges(model, position) {
|
|
465
|
+
const spans = await requestLinkedEdit(model.getValue(), position.lineNumber - 1);
|
|
466
|
+
if (!spans) return null;
|
|
467
|
+
const toRange = (s) => new monaco.Range(s.line + 1, s.start + 1, s.line + 1, s.end + 1);
|
|
468
|
+
return { ranges: [toRange(spans.open), toRange(spans.close)], wordPattern: /[a-z][a-z0-9_]*/i };
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
monaco.languages.registerDocumentSymbolProvider(LANG_ID, {
|
|
472
|
+
async provideDocumentSymbols(model) {
|
|
473
|
+
const lineCount = model.getLineCount();
|
|
474
|
+
const toSymbol = (n) => {
|
|
475
|
+
const start = Math.min(n.line + 1, lineCount);
|
|
476
|
+
const end = Math.min(Math.max(n.endLine, start), lineCount);
|
|
477
|
+
const display = n.name.startsWith("#") ? n.name.replace(/^#+\s*/, "") : n.tag;
|
|
478
|
+
return {
|
|
479
|
+
name: display || n.tag,
|
|
480
|
+
detail: "",
|
|
481
|
+
kind: monaco.languages.SymbolKind.Namespace,
|
|
482
|
+
tags: [],
|
|
483
|
+
range: new monaco.Range(start, 1, end, model.getLineMaxColumn(end)),
|
|
484
|
+
selectionRange: new monaco.Range(start, 1, start, model.getLineMaxColumn(start)),
|
|
485
|
+
children: n.children.map(toSymbol)
|
|
486
|
+
};
|
|
487
|
+
};
|
|
488
|
+
const nodes = await requestDocumentSymbols(model.getValue());
|
|
489
|
+
return nodes.map(toSymbol);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
monaco.languages.registerDefinitionProvider(LANG_ID, {
|
|
493
|
+
async provideDefinition(model, position) {
|
|
494
|
+
const loc = await requestDefinition(model.getValue(), position.lineNumber - 1, position.column - 1);
|
|
495
|
+
if (!loc) return null;
|
|
496
|
+
const tl = loc.line + 1;
|
|
497
|
+
return { uri: model.uri, range: new monaco.Range(tl, 1, tl, model.getLineMaxColumn(tl)) };
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
monaco.languages.registerHoverProvider(LANG_ID, {
|
|
501
|
+
async provideHover(model, position) {
|
|
502
|
+
const line = model.getLineContent(position.lineNumber);
|
|
503
|
+
const profile = profileMarkdown(position.lineNumber - 1);
|
|
504
|
+
const h = line.match(/^#{1,6}\s+(.+?)\s*$/);
|
|
505
|
+
if (h) {
|
|
506
|
+
const type = inferBlockType(h[1] ?? "");
|
|
507
|
+
if (type) {
|
|
508
|
+
const def = BLOCK_TYPES[type];
|
|
509
|
+
return { contents: [{ value: `**${def.label} block**` }, { value: `**Does:** ${def.does}` }, { value: `**Doesn't:** ${def.doesNot}` }] };
|
|
510
|
+
}
|
|
511
|
+
return { contents: [{ value: "**Section**" }] };
|
|
512
|
+
}
|
|
513
|
+
if (/^\s*\/\//.test(line) || /^\s*\/\*/.test(line)) {
|
|
514
|
+
return { contents: [{ value: "Comment \u2014 never sent to the model." }] };
|
|
515
|
+
}
|
|
516
|
+
const md = await requestHover(model.getValue(), position.lineNumber - 1, position.column - 1);
|
|
517
|
+
if (md) return { contents: [{ value: md }, ...profile ? [{ value: profile }] : []] };
|
|
518
|
+
if (profile) return { contents: [{ value: profile }] };
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
monaco.languages.registerDocumentSemanticTokensProvider(LANG_ID, {
|
|
523
|
+
getLegend: () => ({ tokenTypes: [...SEM_LEGEND.tokenTypes], tokenModifiers: [...SEM_LEGEND.tokenModifiers] }),
|
|
524
|
+
provideDocumentSemanticTokens: async (model) => {
|
|
525
|
+
const data = await requestSemanticTokens(model.getValue());
|
|
526
|
+
return { data: new Uint32Array(data) };
|
|
527
|
+
},
|
|
528
|
+
releaseDocumentSemanticTokens: () => void 0
|
|
529
|
+
});
|
|
530
|
+
const KIND = {
|
|
531
|
+
section: monaco.languages.CompletionItemKind.Module,
|
|
532
|
+
variable: monaco.languages.CompletionItemKind.Variable,
|
|
533
|
+
step: monaco.languages.CompletionItemKind.Reference,
|
|
534
|
+
route: monaco.languages.CompletionItemKind.Reference,
|
|
535
|
+
tool: monaco.languages.CompletionItemKind.Function,
|
|
536
|
+
alias: monaco.languages.CompletionItemKind.Reference,
|
|
537
|
+
// a `@@ name:` statement alias — a named pointer, like a step/route
|
|
538
|
+
keyword: monaco.languages.CompletionItemKind.Keyword
|
|
539
|
+
};
|
|
540
|
+
const SNIPPET = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
|
541
|
+
monaco.languages.registerCompletionItemProvider(LANG_ID, {
|
|
542
|
+
// The trigger set is the SHARED source of truth (COMPLETION_TRIGGERS) — the LSP uses the same list, so the
|
|
543
|
+
// web preview and every IDE auto-open completions identically. Space/colon open the value pickers
|
|
544
|
+
// (`$CONFIG modality ␣`, `$REQUIRE variable x:`); safe because completionsAt is context-gated (no rule → []).
|
|
545
|
+
triggerCharacters: [...COMPLETION_TRIGGERS],
|
|
546
|
+
async provideCompletionItems(model, position) {
|
|
547
|
+
const before = model.getValueInRange({ startLineNumber: position.lineNumber, startColumn: 1, endLineNumber: position.lineNumber, endColumn: position.column });
|
|
548
|
+
const struct = await requestCompletion(before, model.getValue(), position.lineNumber - 1, configSchemaCtx);
|
|
549
|
+
return {
|
|
550
|
+
suggestions: struct.map((c) => ({
|
|
551
|
+
label: c.label,
|
|
552
|
+
kind: KIND[c.kind],
|
|
553
|
+
insertText: c.insert,
|
|
554
|
+
detail: c.detail,
|
|
555
|
+
range: new monaco.Range(position.lineNumber, position.column - c.replace, position.lineNumber, position.column),
|
|
556
|
+
...c.snippet ? { insertTextRules: SNIPPET } : {}
|
|
557
|
+
}))
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/web/operators.ts
|
|
564
|
+
var COMMAND_FORMS = [...COMMAND_WORDS].sort((a, b) => b.length - a.length);
|
|
565
|
+
var MARKER_RE = /^(\s*(?:[-*+]\s+|\d+(?:\.\d+)*[.)]\s+)?)([\s\S]*)$/;
|
|
566
|
+
function splitMarker(line) {
|
|
567
|
+
const m = line.match(MARKER_RE);
|
|
568
|
+
return { prefix: m?.[1] ?? "", clause: m?.[2] ?? line };
|
|
569
|
+
}
|
|
570
|
+
function nextListItem(lineText) {
|
|
571
|
+
const unordered = lineText.match(/^(\s*)([-*+])\s+(.*)$/);
|
|
572
|
+
const step = lineText.match(/^(\s*)(\d+(?:\.\d+)*)([.)])\s+(.*)$/);
|
|
573
|
+
if (!unordered && !step) return null;
|
|
574
|
+
const content = unordered?.[3] ?? step?.[4] ?? "";
|
|
575
|
+
if (content.trim() === "") return { terminate: true };
|
|
576
|
+
const indent = unordered?.[1] ?? step?.[1] ?? "";
|
|
577
|
+
if (unordered) return { terminate: false, prefix: `${indent}${unordered[2] ?? "-"} ` };
|
|
578
|
+
const segs = (step?.[2] ?? "0").split(".");
|
|
579
|
+
segs[segs.length - 1] = String(Number(segs[segs.length - 1]) + 1);
|
|
580
|
+
return { terminate: false, prefix: `${indent}${segs.join(".")}${step?.[3] ?? "."} ` };
|
|
581
|
+
}
|
|
582
|
+
function nestStep(lines, line0, outdent) {
|
|
583
|
+
const cur = (lines[line0] ?? "").match(/^(\s*)(\d+(?:\.\d+)*)([.)])(?:\s.*)?$/);
|
|
584
|
+
if (!cur) return null;
|
|
585
|
+
const ord = cur[2] ?? "";
|
|
586
|
+
const delim = cur[3] ?? ".";
|
|
587
|
+
const segs = ord.split(".").map(Number);
|
|
588
|
+
const start = (cur[1] ?? "").length;
|
|
589
|
+
const end = start + ord.length + delim.length;
|
|
590
|
+
if (outdent) {
|
|
591
|
+
if (segs.length === 1) return null;
|
|
592
|
+
const up = segs.slice(0, -1);
|
|
593
|
+
up[up.length - 1] = (up[up.length - 1] ?? 0) + 1;
|
|
594
|
+
return { start, end, text: `${up.join(".")}${delim}` };
|
|
595
|
+
}
|
|
596
|
+
const depth = segs.length;
|
|
597
|
+
let parent = null;
|
|
598
|
+
for (let i = line0 - 1; i >= 0; i -= 1) {
|
|
599
|
+
const t = (lines[i] ?? "").trim();
|
|
600
|
+
if (/^#{1,6}\s/.test(t)) return null;
|
|
601
|
+
const m = t.match(/^(\d+(?:\.\d+)*)[.)]\s/);
|
|
602
|
+
if (!m) continue;
|
|
603
|
+
const d = (m[1] ?? "").split(".").length;
|
|
604
|
+
if (d === depth) {
|
|
605
|
+
parent = m[1] ?? "";
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
if (d < depth) return null;
|
|
609
|
+
}
|
|
610
|
+
if (parent === null) return null;
|
|
611
|
+
const childRe = new RegExp(`^${parent.replace(/\./g, "\\.")}\\.\\d+[.)]\\s`);
|
|
612
|
+
let kids = 0;
|
|
613
|
+
for (let i = line0 - 1; i >= 0; i -= 1) {
|
|
614
|
+
const t = (lines[i] ?? "").trim();
|
|
615
|
+
if (/^#{1,6}\s/.test(t)) break;
|
|
616
|
+
if (childRe.test(t)) kids += 1;
|
|
617
|
+
}
|
|
618
|
+
return { start, end, text: `${parent}.${kids + 1}${delim}` };
|
|
619
|
+
}
|
|
620
|
+
function matchLeadingCommand(clause) {
|
|
621
|
+
if (!clause.startsWith("$")) return null;
|
|
622
|
+
const body = clause.slice(1);
|
|
623
|
+
for (const word of COMMAND_FORMS) {
|
|
624
|
+
if (body.length < word.length) continue;
|
|
625
|
+
if (body.slice(0, word.length).toUpperCase() !== word) continue;
|
|
626
|
+
const next = body[word.length];
|
|
627
|
+
if (next === void 0 || /\s/.test(next) || next === ":") return { len: word.length + 1, upper: `$${word}` };
|
|
628
|
+
}
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
function enclosingBlock(lines, idx) {
|
|
632
|
+
for (let i = Math.min(idx, lines.length) - 1; i >= 0; i -= 1) {
|
|
633
|
+
const m = (lines[i] ?? "").match(/^#{1,6}\s+(.+?)\s*$/);
|
|
634
|
+
if (m) return m[1] ?? "";
|
|
635
|
+
}
|
|
636
|
+
return "";
|
|
637
|
+
}
|
|
638
|
+
function autoCloseSectionTag(head, tail) {
|
|
639
|
+
if (tail.trim() !== "") return null;
|
|
640
|
+
const m = head.match(new RegExp(AUTO_CLOSE_TAG_SRC, "i"));
|
|
641
|
+
return m ? { tag: m[1] ?? "" } : null;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/lib/monaco.ts
|
|
645
|
+
function setupTypeGlish(monaco) {
|
|
646
|
+
setupMonaco(monaco);
|
|
647
|
+
}
|
|
648
|
+
function applyDiagnostics(monaco, model, compileOptions) {
|
|
649
|
+
const sev = monaco.MarkerSeverity;
|
|
650
|
+
monaco.editor.setModelMarkers(
|
|
651
|
+
model,
|
|
652
|
+
LANG_ID,
|
|
653
|
+
compile(model.getValue(), compileOptions).diagnostics.map((d) => ({
|
|
654
|
+
startLineNumber: d.line + 1,
|
|
655
|
+
startColumn: d.start + 1,
|
|
656
|
+
endLineNumber: d.line + 1,
|
|
657
|
+
endColumn: d.end + 1,
|
|
658
|
+
message: d.message,
|
|
659
|
+
source: d.source,
|
|
660
|
+
severity: d.severity === "error" ? sev.Error : d.severity === "warn" ? sev.Warning : sev.Info
|
|
661
|
+
}))
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
function attachTypeGlish(monaco, editor, opts = {}) {
|
|
665
|
+
const subs = [];
|
|
666
|
+
const structDecos = editor.createDecorationsCollection([]);
|
|
667
|
+
if (opts.numberBlankLines !== true) {
|
|
668
|
+
let ordinalKey = "";
|
|
669
|
+
const applyLineOrdinals = () => {
|
|
670
|
+
const model = editor.getModel();
|
|
671
|
+
if (!model) return;
|
|
672
|
+
const ords = lineOrdinals(model.getLinesContent());
|
|
673
|
+
const ordinals = ["", ...ords.map((o) => o === null ? "" : String(o))];
|
|
674
|
+
const key = ords.map((o) => o === null ? "0" : "1").join("");
|
|
675
|
+
if (key === ordinalKey) return;
|
|
676
|
+
ordinalKey = key;
|
|
677
|
+
editor.updateOptions({ lineNumbers: (n) => ordinals[n] ?? "" });
|
|
678
|
+
};
|
|
679
|
+
applyLineOrdinals();
|
|
680
|
+
subs.push(editor.onDidChangeModelContent(applyLineOrdinals));
|
|
681
|
+
}
|
|
682
|
+
const refresh = () => {
|
|
683
|
+
const model = editor.getModel();
|
|
684
|
+
if (!model) return;
|
|
685
|
+
if (opts.structure !== false) {
|
|
686
|
+
const s = structure(model.getValue());
|
|
687
|
+
const decos = [];
|
|
688
|
+
for (const r of s.references) {
|
|
689
|
+
const cls = r.targetId ? "tg-ref" : "tg-ref-dangling";
|
|
690
|
+
decos.push({ range: new monaco.Range(r.loc.line + 1, r.loc.start + 1, r.loc.line + 1, r.loc.end + 1), options: { inlineClassName: cls } });
|
|
691
|
+
}
|
|
692
|
+
structDecos.set(decos);
|
|
693
|
+
}
|
|
694
|
+
if (opts.diagnostics !== false) applyDiagnostics(monaco, model, opts.compileOptions);
|
|
695
|
+
};
|
|
696
|
+
refresh();
|
|
697
|
+
let refreshTimer;
|
|
698
|
+
subs.push(
|
|
699
|
+
editor.onDidChangeModelContent(() => {
|
|
700
|
+
if (refreshTimer !== void 0) clearTimeout(refreshTimer);
|
|
701
|
+
refreshTimer = setTimeout(refresh, 150);
|
|
702
|
+
})
|
|
703
|
+
);
|
|
704
|
+
subs.push({
|
|
705
|
+
dispose: () => {
|
|
706
|
+
if (refreshTimer !== void 0) clearTimeout(refreshTimer);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
if (opts.authoring !== false) {
|
|
710
|
+
editor.updateOptions({ linkedEditing: true, autoIndent: "full", bracketPairColorization: { enabled: false } });
|
|
711
|
+
const pinIndentUnit = () => {
|
|
712
|
+
editor.getModel()?.updateOptions({ tabSize: INDENT_UNIT, insertSpaces: true });
|
|
713
|
+
};
|
|
714
|
+
pinIndentUnit();
|
|
715
|
+
subs.push(editor.onDidChangeModel(pinIndentUnit));
|
|
716
|
+
subs.push(
|
|
717
|
+
editor.onKeyDown((e) => {
|
|
718
|
+
if (e.keyCode === monaco.KeyCode.Enter && !e.shiftKey && !e.altKey) {
|
|
719
|
+
if (handleListEnter(monaco, editor)) {
|
|
720
|
+
e.preventDefault();
|
|
721
|
+
e.stopPropagation();
|
|
722
|
+
}
|
|
723
|
+
} else if (e.keyCode === monaco.KeyCode.Tab) {
|
|
724
|
+
if (handleListTab(monaco, editor, e.shiftKey)) {
|
|
725
|
+
e.preventDefault();
|
|
726
|
+
e.stopPropagation();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
})
|
|
730
|
+
);
|
|
731
|
+
subs.push(
|
|
732
|
+
editor.onDidChangeModelContent((ev) => {
|
|
733
|
+
if (ev.changes.length === 1 && ev.changes[0]) {
|
|
734
|
+
handleTagClose(monaco, editor, ev.changes[0]);
|
|
735
|
+
handleCloseTagOutdent(monaco, editor, ev.changes[0]);
|
|
736
|
+
}
|
|
737
|
+
})
|
|
738
|
+
);
|
|
739
|
+
let recasing = false;
|
|
740
|
+
subs.push(
|
|
741
|
+
editor.onDidChangeModelContent((ev) => {
|
|
742
|
+
if (recasing || ev.changes.length !== 1) return;
|
|
743
|
+
const change = ev.changes[0];
|
|
744
|
+
if (!change) return;
|
|
745
|
+
recasing = true;
|
|
746
|
+
try {
|
|
747
|
+
autoUppercaseCommand(monaco, editor, change);
|
|
748
|
+
} finally {
|
|
749
|
+
recasing = false;
|
|
750
|
+
}
|
|
751
|
+
})
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
if (opts.autoIndent !== false) {
|
|
755
|
+
let renumberTimer;
|
|
756
|
+
let renumbering = false;
|
|
757
|
+
const scheduleRenumber = () => {
|
|
758
|
+
if (renumbering) return;
|
|
759
|
+
if (renumberTimer) clearTimeout(renumberTimer);
|
|
760
|
+
renumberTimer = setTimeout(() => {
|
|
761
|
+
renumbering = true;
|
|
762
|
+
try {
|
|
763
|
+
applyRenumber(monaco, editor);
|
|
764
|
+
} finally {
|
|
765
|
+
renumbering = false;
|
|
766
|
+
}
|
|
767
|
+
}, 120);
|
|
768
|
+
};
|
|
769
|
+
subs.push(
|
|
770
|
+
editor.onDidChangeModelContent((ev) => {
|
|
771
|
+
const ch = ev.changes.length === 1 ? ev.changes[0] : void 0;
|
|
772
|
+
if (ch && completesMarker(monaco, editor, ch)) indentNewLine(monaco, editor, ch.range.startLineNumber);
|
|
773
|
+
if (ch) snapNewbornContent(monaco, editor, ch);
|
|
774
|
+
scheduleRenumber();
|
|
775
|
+
})
|
|
776
|
+
);
|
|
777
|
+
let lastLine = editor.getPosition()?.lineNumber ?? 1;
|
|
778
|
+
subs.push(
|
|
779
|
+
editor.onDidChangeCursorPosition((e) => {
|
|
780
|
+
const departed = lastLine;
|
|
781
|
+
lastLine = e.position.lineNumber;
|
|
782
|
+
if (departed === e.position.lineNumber) return;
|
|
783
|
+
const model = editor.getModel();
|
|
784
|
+
if (!model || departed < 1 || departed > model.getLineCount()) return;
|
|
785
|
+
const text = model.getLineContent(departed);
|
|
786
|
+
if (text.length > 0 && text.trim() === "") {
|
|
787
|
+
editor.executeEdits("tg-reap-whitespace", [
|
|
788
|
+
{ range: new monaco.Range(departed, 1, departed, text.length + 1), text: "" }
|
|
789
|
+
]);
|
|
790
|
+
}
|
|
791
|
+
})
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
return {
|
|
795
|
+
dispose() {
|
|
796
|
+
for (const s of subs) s.dispose();
|
|
797
|
+
structDecos.clear();
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
function applyRenumber(monaco, editor) {
|
|
802
|
+
const model = editor.getModel();
|
|
803
|
+
if (!model) return;
|
|
804
|
+
const oldLines = model.getValue().split("\n");
|
|
805
|
+
const newLines = renumberSubSteps(oldLines);
|
|
806
|
+
if (newLines === oldLines) return;
|
|
807
|
+
const edits = [];
|
|
808
|
+
for (let i = 0; i < oldLines.length; i += 1) {
|
|
809
|
+
const a = oldLines[i] ?? "";
|
|
810
|
+
const b = newLines[i] ?? "";
|
|
811
|
+
if (a === b) continue;
|
|
812
|
+
let p = 0;
|
|
813
|
+
while (p < a.length && p < b.length && a[p] === b[p]) p += 1;
|
|
814
|
+
let s = 0;
|
|
815
|
+
while (s < a.length - p && s < b.length - p && a[a.length - 1 - s] === b[b.length - 1 - s]) s += 1;
|
|
816
|
+
edits.push({ range: new monaco.Range(i + 1, p + 1, i + 1, a.length - s + 1), text: b.slice(p, b.length - s) });
|
|
817
|
+
}
|
|
818
|
+
if (edits.length > 0) editor.executeEdits("tg-renumber", edits);
|
|
819
|
+
}
|
|
820
|
+
function indentNewLine(monaco, editor, lineNumber) {
|
|
821
|
+
const model = editor.getModel();
|
|
822
|
+
if (!model || lineNumber < 1 || lineNumber > model.getLineCount()) return;
|
|
823
|
+
const text = model.getLineContent(lineNumber);
|
|
824
|
+
const trimmed = text.trim();
|
|
825
|
+
if (trimmed === "") return;
|
|
826
|
+
let inFence = false;
|
|
827
|
+
for (let i = 1; i < lineNumber; i += 1) {
|
|
828
|
+
const t = model.getLineContent(i).trim();
|
|
829
|
+
if (t.startsWith("```") || t.startsWith("~~~")) inFence = !inFence;
|
|
830
|
+
}
|
|
831
|
+
if (inFence) return;
|
|
832
|
+
const lines = model.getValue().split("\n");
|
|
833
|
+
if (/^\s*(?:#{1,6}|\d+(?:\.\d+)*[.)]|[-*+])\s*$/i.test(text)) lines[lineNumber - 1] = `${text.replace(/\s+$/, "")} x`;
|
|
834
|
+
const target = layoutColumns(lines).col[lineNumber - 1];
|
|
835
|
+
if (target === null || target === void 0) return;
|
|
836
|
+
const current = text.length - text.trimStart().length;
|
|
837
|
+
if (current !== target) editor.executeEdits("tg-indent-line", [{ range: new monaco.Range(lineNumber, 1, lineNumber, current + 1), text: " ".repeat(target) }]);
|
|
838
|
+
}
|
|
839
|
+
function handleTagClose(monaco, editor, change) {
|
|
840
|
+
if (change.text !== ">" || change.rangeLength !== 0) return;
|
|
841
|
+
const model = editor.getModel();
|
|
842
|
+
if (!model) return;
|
|
843
|
+
const ln = change.range.startLineNumber;
|
|
844
|
+
const caretCol = change.range.startColumn + 1;
|
|
845
|
+
const line = model.getLineContent(ln);
|
|
846
|
+
const close = autoCloseSectionTag(line.slice(0, caretCol - 1), line.slice(caretCol - 1));
|
|
847
|
+
if (!close) return;
|
|
848
|
+
const hypothetical = model.getLinesContent().slice();
|
|
849
|
+
hypothetical.splice(ln - 1, 1, `<${close.tag}>`, "", `</${close.tag}>`);
|
|
850
|
+
const tagIndent = layoutColumns(hypothetical).col[ln - 1] ?? 0;
|
|
851
|
+
const indent = " ".repeat(tagIndent);
|
|
852
|
+
const body = " ".repeat(tagIndent + 2);
|
|
853
|
+
const block = `${indent}<${close.tag}>
|
|
854
|
+
${body}
|
|
855
|
+
${indent}</${close.tag}>`;
|
|
856
|
+
editor.executeEdits(
|
|
857
|
+
"tg-tag-close",
|
|
858
|
+
[{ range: new monaco.Range(ln, 1, ln, model.getLineMaxColumn(ln)), text: block }],
|
|
859
|
+
[new monaco.Selection(ln + 1, body.length + 1, ln + 1, body.length + 1)]
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
function snapNewbornContent(monaco, editor, change) {
|
|
863
|
+
if (change.rangeLength !== 0) return;
|
|
864
|
+
if (change.text === "" || change.text.includes("\n") || change.text.trim() === "") return;
|
|
865
|
+
const model = editor.getModel();
|
|
866
|
+
if (!model) return;
|
|
867
|
+
const ln = change.range.startLineNumber;
|
|
868
|
+
if (change.range.endLineNumber !== ln || ln > model.getLineCount()) return;
|
|
869
|
+
const line = model.getLineContent(ln);
|
|
870
|
+
const before = line.slice(0, change.range.startColumn - 1) + line.slice(change.range.startColumn - 1 + change.text.length);
|
|
871
|
+
if (before.trim() !== "") return;
|
|
872
|
+
const lines = model.getLinesContent();
|
|
873
|
+
const target = layoutColumns(lines).col[ln - 1];
|
|
874
|
+
if (target === null || target === void 0) return;
|
|
875
|
+
const body = line.trim();
|
|
876
|
+
const current = line.length - line.trimStart().length;
|
|
877
|
+
const trailing = line.length - line.trimEnd().length;
|
|
878
|
+
if (current === target && trailing === 0) return;
|
|
879
|
+
const caret = editor.getPosition();
|
|
880
|
+
const caretOffsetInBody = caret && caret.lineNumber === ln ? Math.max(0, caret.column - 1 - current) : body.length;
|
|
881
|
+
editor.executeEdits(
|
|
882
|
+
"tg-newborn-indent",
|
|
883
|
+
[{ range: new monaco.Range(ln, 1, ln, line.length + 1), text: " ".repeat(target) + body }],
|
|
884
|
+
[new monaco.Selection(ln, target + caretOffsetInBody + 1, ln, target + caretOffsetInBody + 1)]
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
function handleCloseTagOutdent(monaco, editor, change) {
|
|
888
|
+
if (change.text !== ">" || change.rangeLength !== 0) return;
|
|
889
|
+
const model = editor.getModel();
|
|
890
|
+
if (!model) return;
|
|
891
|
+
const ln = change.range.startLineNumber;
|
|
892
|
+
const line = model.getLineContent(ln);
|
|
893
|
+
if (!new RegExp(CLOSE_TAG_LINE_SRC, "i").test(line)) return;
|
|
894
|
+
const target = layoutColumns(model.getLinesContent()).col[ln - 1];
|
|
895
|
+
if (target === null || target === void 0) return;
|
|
896
|
+
const current = line.length - line.trimStart().length;
|
|
897
|
+
if (current === target) return;
|
|
898
|
+
editor.executeEdits("tg-close-outdent", [
|
|
899
|
+
{ range: new monaco.Range(ln, 1, ln, current + 1), text: " ".repeat(target) }
|
|
900
|
+
]);
|
|
901
|
+
}
|
|
902
|
+
function completesMarker(monaco, editor, change) {
|
|
903
|
+
if (change.text !== " " || change.rangeLength !== 0) return false;
|
|
904
|
+
const model = editor.getModel();
|
|
905
|
+
if (!model) return false;
|
|
906
|
+
const ln = change.range.startLineNumber;
|
|
907
|
+
const head = model.getValueInRange(new monaco.Range(ln, 1, ln, change.range.startColumn));
|
|
908
|
+
return /^\s*(?:#{1,6}|\d+(?:\.\d+)*[.)]|[-*+])$/i.test(head);
|
|
909
|
+
}
|
|
910
|
+
function handleListEnter(monaco, editor) {
|
|
911
|
+
const model = editor.getModel();
|
|
912
|
+
const pos = editor.getPosition();
|
|
913
|
+
const sel = editor.getSelection();
|
|
914
|
+
if (!model || !pos || !sel) return false;
|
|
915
|
+
const lineNumber = pos.lineNumber;
|
|
916
|
+
const lineText = model.getLineContent(lineNumber);
|
|
917
|
+
const next = nextListItem(lineText);
|
|
918
|
+
if (!next) return false;
|
|
919
|
+
if (next.terminate) {
|
|
920
|
+
editor.executeEdits("list", [{ range: new monaco.Range(lineNumber, 1, lineNumber, lineText.length + 1), text: "" }]);
|
|
921
|
+
return true;
|
|
922
|
+
}
|
|
923
|
+
editor.executeEdits("list", [{ range: sel, text: `
|
|
924
|
+
${next.prefix}` }]);
|
|
925
|
+
return true;
|
|
926
|
+
}
|
|
927
|
+
function handleListTab(monaco, editor, outdent) {
|
|
928
|
+
const model = editor.getModel();
|
|
929
|
+
const pos = editor.getPosition();
|
|
930
|
+
const sel = editor.getSelection();
|
|
931
|
+
if (!model || !pos || sel && !sel.isEmpty()) return false;
|
|
932
|
+
const lineNumber = pos.lineNumber;
|
|
933
|
+
const lineText = model.getLineContent(lineNumber);
|
|
934
|
+
const nest = nestStep(model.getLinesContent(), lineNumber - 1, outdent);
|
|
935
|
+
if (nest) {
|
|
936
|
+
editor.executeEdits("list-nest", [{ range: new monaco.Range(lineNumber, nest.start + 1, lineNumber, nest.end + 1), text: nest.text }]);
|
|
937
|
+
indentNewLine(monaco, editor, lineNumber);
|
|
938
|
+
return true;
|
|
939
|
+
}
|
|
940
|
+
if (/^\s*\d+(?:\.\d+)*[.)]\s/.test(lineText)) return true;
|
|
941
|
+
if (!/^\s*[-*+]\s/.test(lineText)) return false;
|
|
942
|
+
if (outdent) {
|
|
943
|
+
const lead = lineText.length - lineText.trimStart().length;
|
|
944
|
+
const remove = Math.min(2, lead);
|
|
945
|
+
if (remove > 0) {
|
|
946
|
+
editor.executeEdits("list-tab", [{ range: new monaco.Range(lineNumber, 1, lineNumber, remove + 1), text: "" }]);
|
|
947
|
+
editor.setPosition({ lineNumber, column: Math.max(1, pos.column - remove) });
|
|
948
|
+
}
|
|
949
|
+
return true;
|
|
950
|
+
}
|
|
951
|
+
editor.executeEdits("list-tab", [{ range: new monaco.Range(lineNumber, 1, lineNumber, 1), text: " " }]);
|
|
952
|
+
editor.setPosition({ lineNumber, column: pos.column + 2 });
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
955
|
+
function autoUppercaseCommand(monaco, editor, change) {
|
|
956
|
+
if (change.text !== " " || change.rangeLength !== 0) return;
|
|
957
|
+
const model = editor.getModel();
|
|
958
|
+
if (!model) return;
|
|
959
|
+
const ln = change.range.startLineNumber;
|
|
960
|
+
const head = model.getValueInRange(new monaco.Range(ln, 1, ln, change.range.startColumn));
|
|
961
|
+
const { prefix, clause } = splitMarker(head);
|
|
962
|
+
const hit = matchLeadingCommand(clause);
|
|
963
|
+
if (!hit || clause.slice(0, hit.len) === hit.upper) return;
|
|
964
|
+
if (isProseBlock(enclosingBlock(model.getLinesContent(), ln - 1))) return;
|
|
965
|
+
const startColumn = prefix.length + 1;
|
|
966
|
+
editor.executeEdits("auto-uppercase", [{ range: new monaco.Range(ln, startColumn, ln, startColumn + hit.len), text: hit.upper }]);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
export { FILE_EXT, LANG_ID, THEME_ID, applyDiagnostics, attachTypeGlish, setupTypeGlish };
|
|
970
|
+
//# sourceMappingURL=monaco.js.map
|
|
971
|
+
//# sourceMappingURL=monaco.js.map
|