Skip to content

Commit fc502a6

Browse files
authored
Merge pull request #38 from MongooseMoo/moocode-monaco-language
Make the MOO editor syntax-aware
2 parents 951909d + 156a585 commit fc502a6

54 files changed

Lines changed: 18965 additions & 176 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/moocode-language-consolidation.md

Lines changed: 490 additions & 0 deletions
Large diffs are not rendered by default.

docs/moocode-monaco-workstream.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# MOOCode Monaco Workstream
2+
3+
## Goal
4+
5+
Make the simpleedit Monaco editor syntax-aware for ToastStunt/LambdaMOO code while keeping non-code simpleedit buffers plain text.
6+
7+
## Executed Scope
8+
9+
- Register a dedicated Monaco language id: `moocode`.
10+
- Select `moocode` only for simpleedit sessions whose MCP type is `moo-code` or a compatibility alias.
11+
- Keep `string` and `string-list` sessions on Monaco `plaintext`.
12+
- Add a ToastStunt-derived keyword, error constant, builtin variable, and builtin function vocabulary.
13+
- Add Monarch tokenization for:
14+
- statement keywords and block terminators
15+
- builtin variables and functions
16+
- error constants
17+
- object literals
18+
- numbers, strings, string escapes, comments, delimiters, and operators
19+
- Add language configuration for:
20+
- bracket pairs
21+
- auto-closing/surrounding pairs
22+
- statement indentation and outdent patterns
23+
- Add completion items for core keywords, variables, errors, and builtin functions.
24+
- Add hover text for the highest-frequency MOO builtin variables/functions.
25+
- Add lightweight syntax diagnostics for:
26+
- unmatched block close keywords
27+
- missing block close keywords
28+
- misplaced `else`, `elseif`, `except`, and `finally`
29+
- unterminated strings
30+
- unbalanced delimiters
31+
- `break`/`continue` outside `for` or `while`
32+
- Wire diagnostics into Monaco model markers for MOO buffers and clear them for non-MOO buffers.
33+
- Lazy-load the `/editor` route so the Monaco editor wrapper and MOO editor code are not part of the initial app route module.
34+
35+
## Source Authority
36+
37+
The client language vocabulary and grammar shape were derived from:
38+
39+
- `C:/Users/Q/src/toaststunt/moo.grammar`
40+
- `C:/Users/Q/src/toaststunt/src/keywords.gperf`
41+
- `C:/Users/Q/src/toaststunt/src/* register_function(...)` builtin registrations
42+
43+
The current implementation is intentionally a client-side editor aid. The server parser remains the compile authority.
44+
45+
## Future Authority Upgrade
46+
47+
For exact server-equivalent diagnostics, the next workstream should expose or reuse ToastStunt parser output:
48+
49+
- server-side `parse_ast` / validation over MCP or GMCP for connected sessions, or
50+
- a ToastStunt parser compiled to WASM and run in a web worker for local/offline validation.
51+
52+
That would replace the heuristic diagnostics with parser-backed spans while keeping the Monaco registration, completions, hovers, and React plumbing from this workstream.
53+
54+
## Test Gates
55+
56+
Focused TDD gates added:
57+
58+
- `src/editor/moocode/language.test.ts`
59+
- `src/editor/moocode/diagnostics.test.ts`
60+
- `src/components/editor/editorWindow.test.tsx`
61+
- `src/routes.test.tsx`
62+
63+
Acceptance gates:
64+
65+
- focused Vitest suite for the files above
66+
- repo TypeScript typecheck
67+
- production build
68+
- Biome check for the changed source files

package-lock.json

Lines changed: 44 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"livekit-client": "^2.18.7",
2020
"lru-cache": "^10.4.3",
2121
"marked": "^15.0.12",
22-
"monaco-editor": "^0.52.2",
22+
"monaco-editor": "^0.55.1",
2323
"omnitone": "^1.3.0",
2424
"peerjs": "^1.5.5",
2525
"react": "^18.3.1",
@@ -30,8 +30,10 @@
3030
"react-router-dom": "^6.30.3",
3131
"react-use": "^17.6.0",
3232
"strip-ansi": "^7.2.0",
33+
"tree-sitter-moocode": "^0.1.0",
3334
"turndown": "^7.2.4",
3435
"vite-plugin-commit-hash": "^1.0.8",
36+
"web-tree-sitter": "^0.26.9",
3537
"web-vitals": "^3.5.2"
3638
},
3739
"scripts": {

src/components/editor/editor.css

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,194 @@
7474
.editor-statusbar span {
7575
margin: 0 0.5rem;
7676
}
77+
78+
.editor-statusbar-button {
79+
margin: 0 0.5rem;
80+
padding: 0;
81+
border: 0;
82+
background: transparent;
83+
color: #8a3f00;
84+
cursor: pointer;
85+
font: inherit;
86+
text-decoration: underline;
87+
text-underline-offset: 2px;
88+
}
89+
90+
.editor-statusbar-button:focus-visible {
91+
outline: 2px solid #4b8bd6;
92+
outline-offset: 2px;
93+
}
94+
95+
.editor-problems {
96+
flex-shrink: 0;
97+
max-height: 9.5rem;
98+
overflow: auto;
99+
border-top: 1px solid #d6dce5;
100+
background: #fbfcfe;
101+
}
102+
103+
.editor-problems-filters {
104+
display: flex;
105+
gap: 0;
106+
padding: 0.5rem 0.75rem 0;
107+
}
108+
109+
.editor-problems-filter {
110+
min-height: 1.75rem;
111+
padding: 0.2rem 0.65rem;
112+
border: 1px solid #c9d5e7;
113+
border-right-width: 0;
114+
background: #fff;
115+
color: #405167;
116+
cursor: pointer;
117+
font: inherit;
118+
}
119+
120+
.editor-problems-filter:first-child {
121+
border-radius: 4px 0 0 4px;
122+
}
123+
124+
.editor-problems-filter:last-child {
125+
border-right-width: 1px;
126+
border-radius: 0 4px 4px 0;
127+
}
128+
129+
.editor-problems-filter:hover {
130+
background: #f1f5fb;
131+
}
132+
133+
.editor-problems-filter[aria-pressed='true'] {
134+
background: #233143;
135+
color: #fff;
136+
}
137+
138+
.editor-problems-filter:focus-visible {
139+
outline: 2px solid #4b8bd6;
140+
outline-offset: 2px;
141+
}
142+
143+
.editor-problems-fix-all-button {
144+
min-height: 1.75rem;
145+
margin-left: 0.5rem;
146+
padding: 0.2rem 0.65rem;
147+
border: 1px solid #c9d5e7;
148+
border-radius: 4px;
149+
background: #fff;
150+
color: #233143;
151+
cursor: pointer;
152+
font: inherit;
153+
white-space: nowrap;
154+
}
155+
156+
.editor-problems-fix-all-button:hover {
157+
background: #f1f5fb;
158+
}
159+
160+
.editor-problems-fix-all-button:focus-visible {
161+
outline: 2px solid #4b8bd6;
162+
outline-offset: 2px;
163+
}
164+
165+
.editor-problems-list {
166+
display: flex;
167+
flex-direction: column;
168+
gap: 0.25rem;
169+
margin: 0;
170+
padding: 0.5rem 0.75rem;
171+
list-style: none;
172+
}
173+
174+
.editor-problem-row {
175+
display: grid;
176+
grid-template-columns: minmax(0, 1fr) auto;
177+
align-items: center;
178+
gap: 0.5rem;
179+
}
180+
181+
.editor-problem-button {
182+
display: grid;
183+
grid-template-columns: 4.25rem minmax(7rem, 11rem) 6.5rem minmax(0, 1fr);
184+
width: 100%;
185+
min-height: 2rem;
186+
align-items: center;
187+
gap: 0.75rem;
188+
padding: 0.25rem 0.5rem;
189+
border: 1px solid transparent;
190+
border-radius: 4px;
191+
background: transparent;
192+
color: #233143;
193+
cursor: pointer;
194+
font: inherit;
195+
text-align: left;
196+
}
197+
198+
.editor-problem-button:hover {
199+
border-color: #c9d5e7;
200+
background: #f1f5fb;
201+
}
202+
203+
.editor-problem-button:focus-visible {
204+
outline: 2px solid #4b8bd6;
205+
outline-offset: 1px;
206+
}
207+
208+
.editor-problem-fix-button {
209+
min-height: 2rem;
210+
padding: 0.25rem 0.6rem;
211+
border: 1px solid #c9d5e7;
212+
border-radius: 4px;
213+
background: #fff;
214+
color: #233143;
215+
cursor: pointer;
216+
font: inherit;
217+
white-space: nowrap;
218+
}
219+
220+
.editor-problem-fix-button:hover {
221+
background: #f1f5fb;
222+
}
223+
224+
.editor-problem-fix-button:focus-visible {
225+
outline: 2px solid #4b8bd6;
226+
outline-offset: 1px;
227+
}
228+
229+
.editor-problem-severity {
230+
font-weight: 600;
231+
}
232+
233+
.editor-problem-severity-error {
234+
color: #b42318;
235+
}
236+
237+
.editor-problem-severity-warning {
238+
color: #996f00;
239+
}
240+
241+
.editor-problem-code {
242+
min-width: 0;
243+
overflow: hidden;
244+
color: #405167;
245+
font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', monospace;
246+
font-size: 0.9em;
247+
text-overflow: ellipsis;
248+
white-space: nowrap;
249+
}
250+
251+
.editor-problem-location {
252+
color: #5b6472;
253+
white-space: nowrap;
254+
}
255+
256+
.editor-problem-message {
257+
min-width: 0;
258+
overflow: hidden;
259+
text-overflow: ellipsis;
260+
white-space: nowrap;
261+
}
262+
263+
.editor-problems-empty {
264+
margin: 0;
265+
padding: 0.75rem;
266+
color: #5b6472;
267+
}

0 commit comments

Comments
 (0)