Skip to content

Commit c795290

Browse files
committed
Add live editor with CppMode syntax highlighting and WASM compile server
1 parent 2daccf5 commit c795290

6 files changed

Lines changed: 539 additions & 62 deletions

File tree

assets/cm-cppmode.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* CppMode CodeMirror theme colors -- matching Processing Java IDE */
2+
3+
/* FUNCTION1: entry points + constructors -- bold dark blue */
4+
.cm-cppmode-f1 { color: #5b6aff !important; font-weight: bold; }
5+
6+
/* FUNCTION2: API functions -- light blue */
7+
.cm-cppmode-f2 { color: #5ba4ff !important; }
8+
9+
/* KEYWORD3: control flow -- green */
10+
.cm-cppmode-k3 { color: #4ec9a0 !important; }
11+
12+
/* KEYWORD4: constants and built-in vars -- pink/magenta */
13+
.cm-cppmode-k4 { color: #d46fd4 !important; }
14+
15+
/* KEYWORD5: types -- orange */
16+
.cm-cppmode-k5 { color: #e8a262 !important; }
17+
18+
/* KEYWORD6: language keywords -- teal */
19+
.cm-cppmode-k6 { color: #4ec9b0 !important; }

assets/cm-cppmode.js

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
// CodeMirror mode for C++ Mode for Processing
2+
// Matches Processing Java IDE keyword coloring:
3+
// FUNCTION1 = entry points / constructors (bold dark blue, paren-sensitive)
4+
// FUNCTION2 = API functions (light blue, always paren-sensitive)
5+
// KEYWORD3 = control flow (green)
6+
// KEYWORD4 = constants / variables (pink)
7+
// KEYWORD5 = types (orange)
8+
// KEYWORD6 = language keywords (teal)
9+
10+
(function(mod) {
11+
if (typeof exports == "object" && typeof module == "object") mod(require("codemirror"));
12+
else if (typeof define == "function" && define.amd) define(["codemirror"], mod);
13+
else mod(CodeMirror);
14+
})(function(CodeMirror) {
15+
16+
// ── Keyword tables ────────────────────────────────────────────────────────────
17+
// FUNCTION1: colored only when followed by ( -- entry points and color/PVector constructors
18+
const FUNCTION1_PAREN = new Set([
19+
"color","PVector","PImage","PGraphics","PFont","PShape",
20+
"ArrayList","IntList","FloatList","StringList",
21+
"HashMap","HashSet",
22+
]);
23+
// FUNCTION1: always colored (setup/draw/event handlers)
24+
const FUNCTION1_ALWAYS = new Set([
25+
"setup","draw","mousePressed","mouseReleased","mouseClicked",
26+
"mouseMoved","mouseDragged","mouseWheel",
27+
"keyPressed","keyReleased","keyTyped",
28+
"settings",
29+
]);
30+
// FUNCTION2: API functions -- colored only when followed by (
31+
const FUNCTION2 = new Set([
32+
"size","fullScreen","smooth","noSmooth","frameRate","cursor","noCursor","exit",
33+
"background","fill","noFill","stroke","noStroke","strokeWeight","strokeCap","strokeJoin",
34+
"colorMode","lerpColor","red","green","blue","alpha","hue","saturation","brightness",
35+
"rect","ellipse","circle","square","line","point","triangle","quad","arc",
36+
"bezier","bezierVertex","bezierPoint","bezierTangent",
37+
"curve","curveVertex","curvePoint","curveTangent","curveTightness",
38+
"beginShape","endShape","vertex","curveVertex","bezierVertex","quadraticVertex",
39+
"pushMatrix","popMatrix","translate","rotate","rotateX","rotateY","rotateZ",
40+
"scale","shearX","shearY","resetMatrix","applyMatrix","printMatrix",
41+
"pushStyle","popStyle","push","pop",
42+
"image","imageMode","tint","noTint","texture","textureMode","textureWrap",
43+
"text","textSize","textAlign","textLeading","textWidth","textAscent","textDescent",
44+
"textFont","loadFont","createFont",
45+
"loadImage","requestImage","saveFrame","save",
46+
"loadPixels","updatePixels","get","set","copy","mask","filter","blend",
47+
"createGraphics","createImage","createShape",
48+
"lights","noLights","ambientLight","directionalLight","pointLight","spotLight",
49+
"lightFalloff","lightSpecular","normal","shininess","specular","emissive","ambient",
50+
"camera","perspective","ortho","frustum","printCamera","printProjection",
51+
"box","sphere","sphereDetail","beginCamera","endCamera",
52+
"noise","noiseDetail","noiseSeed","random","randomSeed","randomGaussian",
53+
"abs","ceil","floor","round","constrain","dist","lerp","mag","map","max","min","norm","sq","sqrt",
54+
"acos","asin","atan","atan2","cos","degrees","exp","log","pow","radians","sin","tan",
55+
"nf","nfc","nfp","nfs","hex","unhex","binary","unbinary",
56+
"str","int","float","boolean","byte","char","join","split","splitTokens","trim","toLowerCase","toUpperCase",
57+
"append","concat","expand","reverse","shorten","sort","splice","subset",
58+
"year","month","day","hour","minute","second","millis",
59+
"delay","print","println",
60+
"rectMode","ellipseMode","imageMode",
61+
"hint","blendMode","clip","noClip",
62+
"selectInput","selectOutput","selectFolder",
63+
"loadStrings","saveStrings","loadBytes","saveBytes",
64+
"loadJSON","saveJSON","loadXML","saveXML","loadTable","saveTable",
65+
"selectInput","selectOutput",
66+
]);
67+
// KEYWORD3: control flow -- always colored
68+
const KEYWORD3 = new Set([
69+
"if","else","for","while","do","switch","case","break","continue","return",
70+
"default","goto","try","catch","throw","finally",
71+
]);
72+
// KEYWORD4: constants and built-in variables -- always colored
73+
const KEYWORD4 = new Set([
74+
"PI","HALF_PI","QUARTER_PI","TWO_PI","TAU","E","DEG_TO_RAD","RAD_TO_DEG",
75+
"mouseX","mouseY","pmouseX","pmouseY","mouseDX","mouseDY",
76+
"mouseButton","mousePressed","keyPressed","keyCode","key",
77+
"width","height","frameCount","frameRate","displayWidth","displayHeight",
78+
"focused","_frameRate","deltaTime",
79+
"LEFT","RIGHT","CENTER","TOP","BOTTOM","BASELINE",
80+
"CORNER","CORNERS","RADIUS","CENTER",
81+
"POINTS","LINES","TRIANGLES","TRIANGLE_FAN","TRIANGLE_STRIP",
82+
"QUADS","QUAD_STRIP","CLOSE",
83+
"RGB","HSB","HSL","ARGB","ALPHA",
84+
"BLEND","ADD","SUBTRACT","DARKEST","LIGHTEST","DIFFERENCE",
85+
"EXCLUSION","MULTIPLY","SCREEN","OVERLAY","HARD_LIGHT","SOFT_LIGHT",
86+
"DODGE","BURN","REPLACE","REMOVE",
87+
"THRESHOLD","GRAY","INVERT","POSTERIZE","BLUR","OPAQUE","ERODE","DILATE",
88+
"OPEN","CHORD","PIE",
89+
"ROUND","SQUARE","PROJECT","MITER","BEVEL",
90+
"UP","DOWN","ALT","CONTROL","SHIFT","BACKSPACE","TAB","ENTER","RETURN",
91+
"ESC","DELETE","CODED","ARROW_LEFT","ARROW_RIGHT","ARROW_UP","ARROW_DOWN",
92+
"P2D","P3D","JAVA2D","PDF","SVG","DXF","FX2D",
93+
"ENABLE_DEPTH_TEST","DISABLE_DEPTH_TEST",
94+
"ENABLE_DEPTH_MASK","DISABLE_DEPTH_MASK",
95+
"true","false","null","nullptr","NULL",
96+
"WHITE","BLACK","RED","GREEN","BLUE","YELLOW","CYAN","MAGENTA",
97+
]);
98+
// KEYWORD5: types -- always colored
99+
const KEYWORD5 = new Set([
100+
"color","PVector","PImage","PGraphics","PFont","PShape","PApplet",
101+
"ArrayList","IntList","FloatList","StringList","HashMap","HashSet",
102+
"int","float","double","boolean","bool","byte","char","long","short",
103+
"void","String","string","auto","size_t","uint32_t","uint8_t",
104+
]);
105+
// KEYWORD6: language keywords -- always colored
106+
const KEYWORD6 = new Set([
107+
"class","struct","public","private","protected","virtual","override","final",
108+
"static","const","constexpr","inline","explicit","friend","operator",
109+
"new","delete","this","super","extends","implements","import","package",
110+
"namespace","using","typedef","typename","template","sizeof","alignof",
111+
"noexcept","decltype","nullptr","true","false",
112+
"include","define","ifdef","ifndef","endif","pragma",
113+
]);
114+
115+
// ── Token class → CSS class mapping ──────────────────────────────────────────
116+
// We emit custom token names that map to CSS via CodeMirror's token naming
117+
// cm-keyword1 etc aren't standard; we use cm-def for FUNCTION1, etc.
118+
119+
CodeMirror.defineMode("cppmode", function(config) {
120+
var clikeMode = CodeMirror.getMode(config, "text/x-c++src");
121+
122+
return {
123+
startState: function() {
124+
return {
125+
clike: CodeMirror.startState(clikeMode),
126+
lastToken: null,
127+
lastWord: null,
128+
parenNext: false, // true if we just emitted a paren-sensitive word
129+
pendingWord: null, // word waiting to see if ( follows
130+
pendingType: null,
131+
};
132+
},
133+
134+
copyState: function(state) {
135+
return {
136+
clike: CodeMirror.copyState(clikeMode, state.clike),
137+
lastToken: state.lastToken,
138+
lastWord: state.lastWord,
139+
parenNext: state.parenNext,
140+
pendingWord: state.pendingWord,
141+
pendingType: state.pendingType,
142+
};
143+
},
144+
145+
token: function(stream, state) {
146+
// Skip whitespace
147+
if (stream.eatSpace()) {
148+
return null;
149+
}
150+
151+
// Check for pending word -- did ( follow?
152+
if (state.pendingWord) {
153+
var ch = stream.peek();
154+
var word = state.pendingWord;
155+
var type = state.pendingType;
156+
state.pendingWord = null;
157+
state.pendingType = null;
158+
159+
if (ch === '(') {
160+
// Yes -- emit as function
161+
return type === 'f1' ? 'cppmode-f1' : 'cppmode-f2';
162+
} else {
163+
// No paren -- if it's a dual type/function word, emit as type
164+
if (KEYWORD5.has(word)) return 'cppmode-k5';
165+
return null;
166+
}
167+
}
168+
169+
var start = stream.pos;
170+
171+
// Identifiers / keywords
172+
if (stream.match(/^[a-zA-Z_][a-zA-Z0-9_]*/)) {
173+
var word = stream.current();
174+
175+
// KEYWORD3: control flow -- always
176+
if (KEYWORD3.has(word)) return 'cppmode-k3';
177+
178+
// KEYWORD6: language keywords -- always
179+
if (KEYWORD6.has(word)) return 'cppmode-k6';
180+
181+
// KEYWORD4: constants -- always
182+
if (KEYWORD4.has(word)) return 'cppmode-k4';
183+
184+
// FUNCTION1 always (entry points)
185+
if (FUNCTION1_ALWAYS.has(word)) return 'cppmode-f1';
186+
187+
// FUNCTION1 paren-sensitive (constructors)
188+
if (FUNCTION1_PAREN.has(word)) {
189+
// Peek ahead -- but we need to skip whitespace
190+
// Use pending mechanism
191+
state.pendingWord = word;
192+
state.pendingType = 'f1';
193+
// Consume this token now but defer color decision
194+
// We'll decide next token call
195+
return null; // temporarily uncolored -- will be resolved next call
196+
}
197+
198+
// FUNCTION2: paren-sensitive API
199+
if (FUNCTION2.has(word)) {
200+
state.pendingWord = word;
201+
state.pendingType = 'f2';
202+
return null;
203+
}
204+
205+
// KEYWORD5: types -- always (for things not in FUNCTION1_PAREN)
206+
if (KEYWORD5.has(word)) return 'cppmode-k5';
207+
208+
// Fall through to clike for other identifiers
209+
stream.backUp(stream.current().length);
210+
}
211+
212+
// Delegate to clike for everything else (strings, numbers, operators, etc.)
213+
var tok = clikeMode.token(stream, state.clike);
214+
return tok;
215+
},
216+
217+
indent: function(state, textAfter) {
218+
return clikeMode.indent(state.clike, textAfter);
219+
},
220+
221+
electricChars: clikeMode.electricChars,
222+
lineComment: clikeMode.lineComment,
223+
blockCommentStart: clikeMode.blockCommentStart,
224+
blockCommentEnd: clikeMode.blockCommentEnd,
225+
blockCommentContinue: clikeMode.blockCommentContinue,
226+
fold: "brace",
227+
};
228+
});
229+
230+
CodeMirror.defineMIME("text/x-cppmode", "cppmode");
231+
232+
}); // end module

assets/cppmode-keywords.js

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

0 commit comments

Comments
 (0)