tjs-lang 0.2.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/CONTEXT.md +594 -0
- package/LICENSE +190 -0
- package/README.md +220 -0
- package/bin/benchmarks.ts +351 -0
- package/bin/dev.ts +205 -0
- package/bin/docs.js +170 -0
- package/bin/install-cursor.sh +71 -0
- package/bin/install-vscode.sh +71 -0
- package/bin/select-local-models.d.ts +1 -0
- package/bin/select-local-models.js +28 -0
- package/bin/select-local-models.ts +31 -0
- package/demo/autocomplete.test.ts +232 -0
- package/demo/docs.json +186 -0
- package/demo/examples.test.ts +598 -0
- package/demo/index.html +91 -0
- package/demo/src/autocomplete.ts +482 -0
- package/demo/src/capabilities.ts +859 -0
- package/demo/src/demo-nav.ts +2097 -0
- package/demo/src/examples.test.ts +161 -0
- package/demo/src/examples.ts +476 -0
- package/demo/src/imports.test.ts +196 -0
- package/demo/src/imports.ts +421 -0
- package/demo/src/index.ts +639 -0
- package/demo/src/module-store.ts +635 -0
- package/demo/src/module-sw.ts +132 -0
- package/demo/src/playground.ts +949 -0
- package/demo/src/service-host.ts +389 -0
- package/demo/src/settings.ts +440 -0
- package/demo/src/style.ts +280 -0
- package/demo/src/tjs-playground.ts +1605 -0
- package/demo/src/ts-examples.ts +478 -0
- package/demo/src/ts-playground.ts +1092 -0
- package/demo/static/favicon.svg +30 -0
- package/demo/static/photo-1.jpg +0 -0
- package/demo/static/photo-2.jpg +0 -0
- package/demo/static/texts/ai-history.txt +9 -0
- package/demo/static/texts/coffee-origins.txt +9 -0
- package/demo/static/texts/renewable-energy.txt +9 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +37 -0
- package/dist/tjs-batteries.js +4 -0
- package/dist/tjs-batteries.js.map +15 -0
- package/dist/tjs-full.js +256 -0
- package/dist/tjs-full.js.map +37 -0
- package/dist/tjs-transpiler.js +220 -0
- package/dist/tjs-transpiler.js.map +21 -0
- package/dist/tjs-vm.js +4 -0
- package/dist/tjs-vm.js.map +14 -0
- package/docs/CNAME +1 -0
- package/docs/favicon.svg +30 -0
- package/docs/index.html +91 -0
- package/docs/index.js +10468 -0
- package/docs/index.js.map +92 -0
- package/docs/photo-1.jpg +0 -0
- package/docs/photo-1.webp +0 -0
- package/docs/photo-2.jpg +0 -0
- package/docs/photo-2.webp +0 -0
- package/docs/texts/ai-history.txt +9 -0
- package/docs/texts/coffee-origins.txt +9 -0
- package/docs/texts/renewable-energy.txt +9 -0
- package/docs/tjs-lang.svg +31 -0
- package/docs/tosijs-agent.svg +31 -0
- package/editors/README.md +325 -0
- package/editors/ace/ajs-mode.js +328 -0
- package/editors/ace/ajs-mode.ts +269 -0
- package/editors/ajs-syntax.ts +212 -0
- package/editors/build-grammars.ts +510 -0
- package/editors/codemirror/ajs-language.js +287 -0
- package/editors/codemirror/ajs-language.ts +1447 -0
- package/editors/codemirror/autocomplete.test.ts +531 -0
- package/editors/codemirror/component.ts +404 -0
- package/editors/monaco/ajs-monarch.js +243 -0
- package/editors/monaco/ajs-monarch.ts +225 -0
- package/editors/tjs-syntax.ts +115 -0
- package/editors/vscode/language-configuration.json +37 -0
- package/editors/vscode/package.json +65 -0
- package/editors/vscode/syntaxes/ajs-injection.tmLanguage.json +107 -0
- package/editors/vscode/syntaxes/ajs.tmLanguage.json +252 -0
- package/editors/vscode/syntaxes/tjs.tmLanguage.json +333 -0
- package/package.json +83 -0
- package/src/cli/commands/check.ts +41 -0
- package/src/cli/commands/convert.ts +133 -0
- package/src/cli/commands/emit.ts +260 -0
- package/src/cli/commands/run.ts +68 -0
- package/src/cli/commands/test.ts +194 -0
- package/src/cli/commands/types.ts +20 -0
- package/src/cli/create-app.ts +236 -0
- package/src/cli/playground.ts +250 -0
- package/src/cli/tjs.ts +166 -0
- package/src/cli/tjsx.ts +160 -0
- package/tjs-lang.svg +31 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeMirror 6 Web Component
|
|
3
|
+
*
|
|
4
|
+
* A light-DOM web component wrapper for CodeMirror 6.
|
|
5
|
+
* Supports multiple language modes including AJS, TJS, JavaScript, CSS, HTML, and Markdown.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```html
|
|
9
|
+
* <code-mirror mode="tjs">function foo(x: 0) { return x }</code-mirror>
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Attributes:
|
|
13
|
+
* - mode: Language mode (ajs, tjs, js, css, html, markdown)
|
|
14
|
+
* - disabled: Make editor read-only
|
|
15
|
+
* - name: Tab name (for use with tab-selector)
|
|
16
|
+
*
|
|
17
|
+
* Properties:
|
|
18
|
+
* - value: Get/set editor content
|
|
19
|
+
* - editor: Access the underlying CodeMirror EditorView
|
|
20
|
+
*
|
|
21
|
+
* Events:
|
|
22
|
+
* - change: Fired when content changes, detail contains { value }
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { Component, ElementCreator } from 'tosijs'
|
|
26
|
+
import { EditorView, minimalSetup } from 'codemirror'
|
|
27
|
+
import { EditorState, Extension, Compartment } from '@codemirror/state'
|
|
28
|
+
import { javascript } from '@codemirror/lang-javascript'
|
|
29
|
+
import { css } from '@codemirror/lang-css'
|
|
30
|
+
import { html } from '@codemirror/lang-html'
|
|
31
|
+
import { markdown } from '@codemirror/lang-markdown'
|
|
32
|
+
import { oneDark } from '@codemirror/theme-one-dark'
|
|
33
|
+
import {
|
|
34
|
+
lineNumbers,
|
|
35
|
+
highlightActiveLineGutter,
|
|
36
|
+
highlightSpecialChars,
|
|
37
|
+
drawSelection,
|
|
38
|
+
dropCursor,
|
|
39
|
+
rectangularSelection,
|
|
40
|
+
crosshairCursor,
|
|
41
|
+
highlightActiveLine,
|
|
42
|
+
keymap,
|
|
43
|
+
} from '@codemirror/view'
|
|
44
|
+
import {
|
|
45
|
+
foldGutter,
|
|
46
|
+
indentOnInput,
|
|
47
|
+
syntaxHighlighting,
|
|
48
|
+
defaultHighlightStyle,
|
|
49
|
+
bracketMatching,
|
|
50
|
+
foldKeymap,
|
|
51
|
+
} from '@codemirror/language'
|
|
52
|
+
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands'
|
|
53
|
+
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
|
|
54
|
+
import {
|
|
55
|
+
closeBrackets,
|
|
56
|
+
closeBracketsKeymap,
|
|
57
|
+
completionKeymap,
|
|
58
|
+
} from '@codemirror/autocomplete'
|
|
59
|
+
import {
|
|
60
|
+
lintKeymap,
|
|
61
|
+
lintGutter,
|
|
62
|
+
setDiagnostics,
|
|
63
|
+
Diagnostic,
|
|
64
|
+
} from '@codemirror/lint'
|
|
65
|
+
import {
|
|
66
|
+
ajsEditorExtension,
|
|
67
|
+
tjsEditorExtension,
|
|
68
|
+
AutocompleteConfig,
|
|
69
|
+
} from './ajs-language'
|
|
70
|
+
|
|
71
|
+
// Custom setup without autocompletion (we add our own via ajsEditorExtension)
|
|
72
|
+
// Based on basicSetup but excludes autocompletion()
|
|
73
|
+
const customSetup: Extension = [
|
|
74
|
+
lineNumbers(),
|
|
75
|
+
highlightActiveLineGutter(),
|
|
76
|
+
highlightSpecialChars(),
|
|
77
|
+
history(),
|
|
78
|
+
foldGutter(),
|
|
79
|
+
drawSelection(),
|
|
80
|
+
dropCursor(),
|
|
81
|
+
EditorState.allowMultipleSelections.of(true),
|
|
82
|
+
indentOnInput(),
|
|
83
|
+
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
84
|
+
bracketMatching(),
|
|
85
|
+
closeBrackets(),
|
|
86
|
+
rectangularSelection(),
|
|
87
|
+
crosshairCursor(),
|
|
88
|
+
highlightActiveLine(),
|
|
89
|
+
highlightSelectionMatches(),
|
|
90
|
+
keymap.of([
|
|
91
|
+
...closeBracketsKeymap,
|
|
92
|
+
...defaultKeymap,
|
|
93
|
+
...searchKeymap,
|
|
94
|
+
...historyKeymap,
|
|
95
|
+
...foldKeymap,
|
|
96
|
+
...completionKeymap,
|
|
97
|
+
...lintKeymap,
|
|
98
|
+
]),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
// Note: Compartments must be per-instance, not module-level
|
|
102
|
+
// Each editor needs its own compartments to avoid interference
|
|
103
|
+
|
|
104
|
+
// Map of mode names to extensions
|
|
105
|
+
function getLanguageExtension(
|
|
106
|
+
mode: string,
|
|
107
|
+
autocomplete?: AutocompleteConfig
|
|
108
|
+
): Extension {
|
|
109
|
+
switch (mode) {
|
|
110
|
+
case 'ajs':
|
|
111
|
+
return ajsEditorExtension({ autocomplete })
|
|
112
|
+
case 'tjs':
|
|
113
|
+
// TJS is less restrictive than AJS - allows import/export, async/await, throw
|
|
114
|
+
return tjsEditorExtension({ autocomplete })
|
|
115
|
+
case 'js':
|
|
116
|
+
case 'javascript':
|
|
117
|
+
return javascript()
|
|
118
|
+
case 'ts':
|
|
119
|
+
case 'typescript':
|
|
120
|
+
return javascript({ typescript: true })
|
|
121
|
+
case 'css':
|
|
122
|
+
return css()
|
|
123
|
+
case 'html':
|
|
124
|
+
return html()
|
|
125
|
+
case 'md':
|
|
126
|
+
case 'markdown':
|
|
127
|
+
return markdown()
|
|
128
|
+
default:
|
|
129
|
+
return javascript()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export class CodeMirror extends Component {
|
|
134
|
+
// Shadow DOM styles - matching xin-codemirror blueprint pattern
|
|
135
|
+
static styleSpec = {
|
|
136
|
+
':host': {
|
|
137
|
+
display: 'block',
|
|
138
|
+
width: '100%',
|
|
139
|
+
height: '100%',
|
|
140
|
+
position: 'relative',
|
|
141
|
+
textAlign: 'left',
|
|
142
|
+
fontSize: '14px',
|
|
143
|
+
overflow: 'hidden',
|
|
144
|
+
// Let CodeMirror theme control background, or fall back to transparent
|
|
145
|
+
backgroundColor: 'transparent',
|
|
146
|
+
},
|
|
147
|
+
'.cm-editor': {
|
|
148
|
+
height: '100%',
|
|
149
|
+
},
|
|
150
|
+
'.cm-scroller': {
|
|
151
|
+
outline: 'none',
|
|
152
|
+
fontFamily: "Menlo, Monaco, Consolas, 'Courier New', monospace",
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private _source: string = ''
|
|
157
|
+
private _editor: EditorView | undefined
|
|
158
|
+
private _autocompleteConfig: AutocompleteConfig = {}
|
|
159
|
+
private _darkModeObserver: MutationObserver | null = null
|
|
160
|
+
|
|
161
|
+
// Per-instance compartments for language, readonly state, and theme
|
|
162
|
+
private languageCompartment = new Compartment()
|
|
163
|
+
private readonlyCompartment = new Compartment()
|
|
164
|
+
private themeCompartment = new Compartment()
|
|
165
|
+
|
|
166
|
+
mode = 'javascript'
|
|
167
|
+
disabled = false
|
|
168
|
+
role = 'code editor'
|
|
169
|
+
|
|
170
|
+
/** Configure autocomplete callbacks for metadata extraction */
|
|
171
|
+
set autocomplete(config: AutocompleteConfig) {
|
|
172
|
+
this._autocompleteConfig = config
|
|
173
|
+
// Reconfigure if editor exists
|
|
174
|
+
if (this._editor) {
|
|
175
|
+
this._editor.dispatch({
|
|
176
|
+
effects: [
|
|
177
|
+
this.languageCompartment.reconfigure(
|
|
178
|
+
getLanguageExtension(this.mode, this._autocompleteConfig)
|
|
179
|
+
),
|
|
180
|
+
],
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
get autocomplete(): AutocompleteConfig {
|
|
186
|
+
return this._autocompleteConfig
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
get value(): string {
|
|
190
|
+
return this._editor !== undefined
|
|
191
|
+
? this._editor.state.doc.toString()
|
|
192
|
+
: this._source
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
set value(text: string) {
|
|
196
|
+
if (this._editor !== undefined) {
|
|
197
|
+
const currentValue = this._editor.state.doc.toString()
|
|
198
|
+
if (currentValue !== text) {
|
|
199
|
+
this._editor.dispatch({
|
|
200
|
+
changes: {
|
|
201
|
+
from: 0,
|
|
202
|
+
to: this._editor.state.doc.length,
|
|
203
|
+
insert: text,
|
|
204
|
+
},
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
this._source = text
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
get editor(): EditorView | undefined {
|
|
213
|
+
return this._editor
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
constructor() {
|
|
217
|
+
super()
|
|
218
|
+
this.initAttributes('mode', 'disabled')
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private isDarkMode(): boolean {
|
|
222
|
+
return document.body.classList.contains('darkmode')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private getThemeExtension(): Extension {
|
|
226
|
+
return this.isDarkMode() ? oneDark : []
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private updateTheme() {
|
|
230
|
+
if (!this._editor) return
|
|
231
|
+
this._editor.dispatch({
|
|
232
|
+
effects: this.themeCompartment.reconfigure(this.getThemeExtension()),
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
connectedCallback() {
|
|
237
|
+
super.connectedCallback()
|
|
238
|
+
|
|
239
|
+
// Get initial value from textContent if not already set
|
|
240
|
+
if (
|
|
241
|
+
this._source === '' &&
|
|
242
|
+
this.textContent &&
|
|
243
|
+
this.textContent.trim().length > 0
|
|
244
|
+
) {
|
|
245
|
+
this._source = this.textContent.trim()
|
|
246
|
+
this.textContent = ''
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!this._editor) {
|
|
250
|
+
this.createEditor()
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private createEditor() {
|
|
255
|
+
const startState = EditorState.create({
|
|
256
|
+
doc: this._source,
|
|
257
|
+
extensions: [
|
|
258
|
+
customSetup,
|
|
259
|
+
lintGutter(),
|
|
260
|
+
this.languageCompartment.of(
|
|
261
|
+
getLanguageExtension(this.mode, this._autocompleteConfig)
|
|
262
|
+
),
|
|
263
|
+
this.readonlyCompartment.of(EditorState.readOnly.of(this.disabled)),
|
|
264
|
+
this.themeCompartment.of(this.getThemeExtension()),
|
|
265
|
+
EditorView.updateListener.of((update) => {
|
|
266
|
+
if (update.docChanged) {
|
|
267
|
+
this.dispatchEvent(new Event('change', { bubbles: true }))
|
|
268
|
+
}
|
|
269
|
+
}),
|
|
270
|
+
],
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
this._editor = new EditorView({
|
|
274
|
+
state: startState,
|
|
275
|
+
parent: this.shadowRoot || this,
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// Watch for dark mode changes on body
|
|
279
|
+
this._darkModeObserver = new MutationObserver((mutations) => {
|
|
280
|
+
for (const mutation of mutations) {
|
|
281
|
+
if (mutation.attributeName === 'class') {
|
|
282
|
+
this.updateTheme()
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
this._darkModeObserver.observe(document.body, { attributes: true })
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
disconnectedCallback() {
|
|
290
|
+
super.disconnectedCallback()
|
|
291
|
+
this._darkModeObserver?.disconnect()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
onResize() {
|
|
295
|
+
if (this._editor) this._editor.requestMeasure()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
render(): void {
|
|
299
|
+
super.render()
|
|
300
|
+
|
|
301
|
+
// Update language and readonly state when properties change
|
|
302
|
+
if (this._editor) {
|
|
303
|
+
this._editor.dispatch({
|
|
304
|
+
effects: [
|
|
305
|
+
this.languageCompartment.reconfigure(
|
|
306
|
+
getLanguageExtension(this.mode, this._autocompleteConfig)
|
|
307
|
+
),
|
|
308
|
+
this.readonlyCompartment.reconfigure(
|
|
309
|
+
EditorState.readOnly.of(this.disabled)
|
|
310
|
+
),
|
|
311
|
+
],
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Focus the editor
|
|
317
|
+
focus() {
|
|
318
|
+
this._editor?.focus()
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Insert text at cursor
|
|
322
|
+
insert(text: string) {
|
|
323
|
+
if (!this._editor) return
|
|
324
|
+
const { from } = this._editor.state.selection.main
|
|
325
|
+
this._editor.dispatch({
|
|
326
|
+
changes: { from, insert: text },
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Get selected text
|
|
331
|
+
getSelection(): string {
|
|
332
|
+
if (!this._editor) return ''
|
|
333
|
+
const { from, to } = this._editor.state.selection.main
|
|
334
|
+
return this._editor.state.doc.sliceString(from, to)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Replace selection
|
|
338
|
+
replaceSelection(text: string) {
|
|
339
|
+
if (!this._editor) return
|
|
340
|
+
const { from, to } = this._editor.state.selection.main
|
|
341
|
+
this._editor.dispatch({
|
|
342
|
+
changes: { from, to, insert: text },
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Go to a specific line (1-indexed) and optionally column
|
|
347
|
+
goToLine(line: number, column: number = 1) {
|
|
348
|
+
if (!this._editor) return
|
|
349
|
+
const doc = this._editor.state.doc
|
|
350
|
+
// Clamp line to valid range
|
|
351
|
+
const lineNum = Math.max(1, Math.min(line, doc.lines))
|
|
352
|
+
const lineInfo = doc.line(lineNum)
|
|
353
|
+
// Calculate position: start of line + column offset (clamped)
|
|
354
|
+
const col = Math.max(1, Math.min(column, lineInfo.length + 1))
|
|
355
|
+
const pos = lineInfo.from + col - 1
|
|
356
|
+
|
|
357
|
+
this._editor.dispatch({
|
|
358
|
+
selection: { anchor: pos },
|
|
359
|
+
scrollIntoView: true,
|
|
360
|
+
})
|
|
361
|
+
this._editor.focus()
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Set diagnostics (errors/warnings) to show in the gutter
|
|
365
|
+
// Each marker has: line (1-indexed), message, severity ('error' | 'warning' | 'info')
|
|
366
|
+
setMarkers(
|
|
367
|
+
markers: Array<{
|
|
368
|
+
line: number
|
|
369
|
+
message: string
|
|
370
|
+
severity?: 'error' | 'warning' | 'info'
|
|
371
|
+
}>
|
|
372
|
+
) {
|
|
373
|
+
if (!this._editor) return
|
|
374
|
+
const doc = this._editor.state.doc
|
|
375
|
+
|
|
376
|
+
const diagnostics: Diagnostic[] = markers
|
|
377
|
+
.filter((m) => m.line >= 1 && m.line <= doc.lines)
|
|
378
|
+
.map((m) => {
|
|
379
|
+
const lineInfo = doc.line(m.line)
|
|
380
|
+
return {
|
|
381
|
+
from: lineInfo.from,
|
|
382
|
+
to: lineInfo.to,
|
|
383
|
+
severity: m.severity || 'error',
|
|
384
|
+
message: m.message,
|
|
385
|
+
}
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
this._editor.dispatch(setDiagnostics(this._editor.state, diagnostics))
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Clear all diagnostics
|
|
392
|
+
clearMarkers() {
|
|
393
|
+
if (!this._editor) return
|
|
394
|
+
this._editor.dispatch(setDiagnostics(this._editor.state, []))
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Element creator for tosijs - shadow DOM for proper CodeMirror style encapsulation
|
|
399
|
+
// CodeMirror's StyleModule.mount needs to inject styles into the shadow root
|
|
400
|
+
export const codeMirror: ElementCreator<CodeMirror> = CodeMirror.elementCreator(
|
|
401
|
+
{
|
|
402
|
+
tag: 'code-mirror',
|
|
403
|
+
}
|
|
404
|
+
)
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monaco Editor Monarch Tokenizer for AsyncJS
|
|
3
|
+
*
|
|
4
|
+
* Usage in browser:
|
|
5
|
+
* ```html
|
|
6
|
+
* <script src="https://unpkg.com/monaco-editor/min/vs/loader.js"></script>
|
|
7
|
+
* <script src="ajs-monarch.js"></script>
|
|
8
|
+
* <script>
|
|
9
|
+
* require(['vs/editor/editor.main'], function() {
|
|
10
|
+
* registerAjsLanguage(monaco);
|
|
11
|
+
* monaco.editor.create(document.getElementById('container'), {
|
|
12
|
+
* value: 'function agent(topic: "string") { ... }',
|
|
13
|
+
* language: 'ajs'
|
|
14
|
+
* });
|
|
15
|
+
* });
|
|
16
|
+
* </script>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const ajsLanguageId = 'ajs'
|
|
21
|
+
|
|
22
|
+
const ajsLanguageConfiguration = {
|
|
23
|
+
comments: {
|
|
24
|
+
lineComment: '//',
|
|
25
|
+
blockComment: ['/*', '*/'],
|
|
26
|
+
},
|
|
27
|
+
brackets: [
|
|
28
|
+
['{', '}'],
|
|
29
|
+
['[', ']'],
|
|
30
|
+
['(', ')'],
|
|
31
|
+
],
|
|
32
|
+
autoClosingPairs: [
|
|
33
|
+
{ open: '{', close: '}' },
|
|
34
|
+
{ open: '[', close: ']' },
|
|
35
|
+
{ open: '(', close: ')' },
|
|
36
|
+
{ open: "'", close: "'", notIn: ['string', 'comment'] },
|
|
37
|
+
{ open: '"', close: '"', notIn: ['string', 'comment'] },
|
|
38
|
+
{ open: '`', close: '`', notIn: ['string', 'comment'] },
|
|
39
|
+
],
|
|
40
|
+
surroundingPairs: [
|
|
41
|
+
{ open: '{', close: '}' },
|
|
42
|
+
{ open: '[', close: ']' },
|
|
43
|
+
{ open: '(', close: ')' },
|
|
44
|
+
{ open: "'", close: "'" },
|
|
45
|
+
{ open: '"', close: '"' },
|
|
46
|
+
{ open: '`', close: '`' },
|
|
47
|
+
],
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const ajsMonarchLanguage = {
|
|
51
|
+
defaultToken: 'source',
|
|
52
|
+
ignoreCase: false,
|
|
53
|
+
|
|
54
|
+
keywords: [
|
|
55
|
+
'function',
|
|
56
|
+
'return',
|
|
57
|
+
'if',
|
|
58
|
+
'else',
|
|
59
|
+
'while',
|
|
60
|
+
'for',
|
|
61
|
+
'of',
|
|
62
|
+
'in',
|
|
63
|
+
'try',
|
|
64
|
+
'catch',
|
|
65
|
+
'finally',
|
|
66
|
+
'throw',
|
|
67
|
+
'let',
|
|
68
|
+
'const',
|
|
69
|
+
'true',
|
|
70
|
+
'false',
|
|
71
|
+
'null',
|
|
72
|
+
],
|
|
73
|
+
|
|
74
|
+
// Forbidden keywords - highlighted as errors
|
|
75
|
+
forbidden: [
|
|
76
|
+
'new',
|
|
77
|
+
'class',
|
|
78
|
+
'async',
|
|
79
|
+
'await',
|
|
80
|
+
'var',
|
|
81
|
+
'this',
|
|
82
|
+
'super',
|
|
83
|
+
'extends',
|
|
84
|
+
'implements',
|
|
85
|
+
'interface',
|
|
86
|
+
'type',
|
|
87
|
+
'yield',
|
|
88
|
+
'import',
|
|
89
|
+
'export',
|
|
90
|
+
'require',
|
|
91
|
+
],
|
|
92
|
+
|
|
93
|
+
typeKeywords: ['Date', 'Set', 'Map', 'Array', 'Object', 'String', 'Number'],
|
|
94
|
+
|
|
95
|
+
operators: [
|
|
96
|
+
'=',
|
|
97
|
+
'>',
|
|
98
|
+
'<',
|
|
99
|
+
'!',
|
|
100
|
+
'~',
|
|
101
|
+
'?',
|
|
102
|
+
':',
|
|
103
|
+
'==',
|
|
104
|
+
'<=',
|
|
105
|
+
'>=',
|
|
106
|
+
'!=',
|
|
107
|
+
'&&',
|
|
108
|
+
'||',
|
|
109
|
+
'++',
|
|
110
|
+
'--',
|
|
111
|
+
'+',
|
|
112
|
+
'-',
|
|
113
|
+
'*',
|
|
114
|
+
'/',
|
|
115
|
+
'&',
|
|
116
|
+
'|',
|
|
117
|
+
'^',
|
|
118
|
+
'%',
|
|
119
|
+
],
|
|
120
|
+
|
|
121
|
+
symbols: /[=><!~?:&|+\-*\/\^%]+/,
|
|
122
|
+
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4})/,
|
|
123
|
+
|
|
124
|
+
tokenizer: {
|
|
125
|
+
root: [
|
|
126
|
+
[
|
|
127
|
+
/[a-zA-Z_$][\w$]*/,
|
|
128
|
+
{
|
|
129
|
+
cases: {
|
|
130
|
+
'@forbidden': 'invalid',
|
|
131
|
+
'@keywords': 'keyword',
|
|
132
|
+
'@typeKeywords': 'type.identifier',
|
|
133
|
+
'@default': 'identifier',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
|
|
138
|
+
{ include: '@whitespace' },
|
|
139
|
+
|
|
140
|
+
[/[{}()\[\]]/, '@brackets'],
|
|
141
|
+
[
|
|
142
|
+
/@symbols/,
|
|
143
|
+
{
|
|
144
|
+
cases: {
|
|
145
|
+
'@operators': 'operator',
|
|
146
|
+
'@default': '',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
|
|
151
|
+
[/\d*\.\d+([eE][\-+]?\d+)?/, 'number.float'],
|
|
152
|
+
[/0[xX][0-9a-fA-F]+/, 'number.hex'],
|
|
153
|
+
[/\d+/, 'number'],
|
|
154
|
+
|
|
155
|
+
[/[;,.]/, 'delimiter'],
|
|
156
|
+
|
|
157
|
+
[/'/, { token: 'string.quote', bracket: '@open', next: '@stringSingle' }],
|
|
158
|
+
[/"/, { token: 'string.quote', bracket: '@open', next: '@stringDouble' }],
|
|
159
|
+
[
|
|
160
|
+
/`/,
|
|
161
|
+
{ token: 'string.quote', bracket: '@open', next: '@stringBacktick' },
|
|
162
|
+
],
|
|
163
|
+
],
|
|
164
|
+
|
|
165
|
+
stringSingle: [
|
|
166
|
+
[/[^\\']+/, 'string'],
|
|
167
|
+
[/@escapes/, 'string.escape'],
|
|
168
|
+
[/\\./, 'string.escape.invalid'],
|
|
169
|
+
[/'/, { token: 'string.quote', bracket: '@close', next: '@pop' }],
|
|
170
|
+
],
|
|
171
|
+
|
|
172
|
+
stringDouble: [
|
|
173
|
+
[/[^\\"]+/, 'string'],
|
|
174
|
+
[/@escapes/, 'string.escape'],
|
|
175
|
+
[/\\./, 'string.escape.invalid'],
|
|
176
|
+
[/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }],
|
|
177
|
+
],
|
|
178
|
+
|
|
179
|
+
stringBacktick: [
|
|
180
|
+
[/[^\\`$]+/, 'string'],
|
|
181
|
+
[/@escapes/, 'string.escape'],
|
|
182
|
+
[/\\./, 'string.escape.invalid'],
|
|
183
|
+
[
|
|
184
|
+
/\$\{/,
|
|
185
|
+
{ token: 'delimiter.bracket', next: '@stringTemplateExpression' },
|
|
186
|
+
],
|
|
187
|
+
[/`/, { token: 'string.quote', bracket: '@close', next: '@pop' }],
|
|
188
|
+
],
|
|
189
|
+
|
|
190
|
+
stringTemplateExpression: [
|
|
191
|
+
[/[^}]+/, 'identifier'],
|
|
192
|
+
[/\}/, { token: 'delimiter.bracket', next: '@pop' }],
|
|
193
|
+
],
|
|
194
|
+
|
|
195
|
+
whitespace: [
|
|
196
|
+
[/[ \t\r\n]+/, 'white'],
|
|
197
|
+
[/\/\*\*(?!\/)/, 'comment.doc', '@docComment'],
|
|
198
|
+
[/\/\*/, 'comment', '@comment'],
|
|
199
|
+
[/\/\/.*$/, 'comment'],
|
|
200
|
+
],
|
|
201
|
+
|
|
202
|
+
comment: [
|
|
203
|
+
[/[^\/*]+/, 'comment'],
|
|
204
|
+
[/\*\//, 'comment', '@pop'],
|
|
205
|
+
[/[\/*]/, 'comment'],
|
|
206
|
+
],
|
|
207
|
+
|
|
208
|
+
docComment: [
|
|
209
|
+
[/@\w+/, 'comment.doc.tag'],
|
|
210
|
+
[/[^\/*]+/, 'comment.doc'],
|
|
211
|
+
[/\*\//, 'comment.doc', '@pop'],
|
|
212
|
+
[/[\/*]/, 'comment.doc'],
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Register AsyncJS language with Monaco editor
|
|
219
|
+
* @param {typeof import('monaco-editor')} monaco
|
|
220
|
+
*/
|
|
221
|
+
function registerAjsLanguage(monaco) {
|
|
222
|
+
monaco.languages.register({ id: ajsLanguageId })
|
|
223
|
+
monaco.languages.setLanguageConfiguration(
|
|
224
|
+
ajsLanguageId,
|
|
225
|
+
ajsLanguageConfiguration
|
|
226
|
+
)
|
|
227
|
+
monaco.languages.setMonarchTokensProvider(ajsLanguageId, ajsMonarchLanguage)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Export for different module systems
|
|
231
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
232
|
+
module.exports = {
|
|
233
|
+
languageId: ajsLanguageId,
|
|
234
|
+
languageConfiguration: ajsLanguageConfiguration,
|
|
235
|
+
monarchLanguage: ajsMonarchLanguage,
|
|
236
|
+
registerAjsLanguage,
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (typeof window !== 'undefined') {
|
|
241
|
+
window.registerAjsLanguage = registerAjsLanguage
|
|
242
|
+
window.ajsMonarchLanguage = ajsMonarchLanguage
|
|
243
|
+
}
|