Skip to content

Commit a169bb5

Browse files
committed
Add syntax highlighting to playground editor
Implements real-time C syntax highlighting with VS Code Dark+ color scheme. Features: - Protected replacement algorithm prevents highlighting conflicts - Highlights keywords (int, void, if, return) in blue - Strings in orange, comments in green italic - Numbers in light green, functions in yellow - Preprocessor directives (#include) in purple - Special null-safety keywords (_Nullable, _Nonnull) highlighted - Dual-layer editor with transparent textarea over highlighted div - Synchronized scrolling between layers - Updates in real-time as user types Technical implementation: - Stores strings/comments as placeholders during processing - Applies keyword/number/function highlighting to safe content - Restores protected content to prevent regex conflicts - VS Code Dark+ theme colors for familiarity
1 parent c89276e commit a169bb5

File tree

1 file changed

+122
-4
lines changed

1 file changed

+122
-4
lines changed

nullsafe-playground/index.html

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,20 +276,60 @@
276276
}
277277

278278
#editor {
279-
flex: 1;
279+
position: absolute;
280+
top: 0;
281+
left: 0;
282+
right: 0;
283+
bottom: 0;
280284
padding: 20px;
281285
font-family: 'Menlo', 'Monaco', 'Consolas', 'Courier New', monospace;
282286
font-size: 14px;
283287
line-height: 1.7;
284-
background: var(--bg-primary);
285-
color: var(--text-primary);
288+
background: transparent;
289+
color: transparent;
286290
border: none;
287291
outline: none;
288292
resize: none;
289293
tab-size: 4;
290294
overflow-y: auto;
295+
caret-color: white;
296+
z-index: 2;
297+
}
298+
299+
#editor-wrapper {
300+
flex: 1;
301+
position: relative;
302+
overflow: hidden;
291303
}
292304

305+
#editor-highlight {
306+
position: absolute;
307+
top: 0;
308+
left: 0;
309+
right: 0;
310+
bottom: 0;
311+
padding: 20px;
312+
font-family: 'Menlo', 'Monaco', 'Consolas', 'Courier New', monospace;
313+
font-size: 14px;
314+
line-height: 1.7;
315+
white-space: pre-wrap;
316+
word-wrap: break-word;
317+
overflow-y: auto;
318+
pointer-events: none;
319+
z-index: 1;
320+
}
321+
322+
/* Syntax highlighting colors - VS Code Dark+ theme */
323+
.hljs-keyword { color: #569cd6; } /* Blue for keywords */
324+
.hljs-type { color: #4ec9b0; } /* Teal for types */
325+
.hljs-string { color: #ce9178; } /* Orange for strings */
326+
.hljs-number { color: #b5cea8; } /* Light green for numbers */
327+
.hljs-comment { color: #6a9955; font-style: italic; } /* Green for comments */
328+
.hljs-function { color: #dcdcaa; } /* Yellow for functions */
329+
.hljs-preprocessor { color: #c586c0; } /* Purple for preprocessor */
330+
.hljs-operator { color: #d4d4d4; }
331+
.hljs-punctuation { color: #d4d4d4; }
332+
293333
#output {
294334
flex: 1;
295335
padding: 20px;
@@ -422,7 +462,10 @@ <h1>Null-Safe C Playground</h1>
422462
<button class="icon-btn" title="Keyboard shortcut: Cmd/Ctrl+Enter">⌨️ Ctrl+Enter to compile</button>
423463
</div>
424464
</div>
425-
<textarea id="editor" spellcheck="false" placeholder="Write your C code here..."></textarea>
465+
<div id="editor-wrapper">
466+
<div id="editor-highlight"></div>
467+
<textarea id="editor" spellcheck="false" placeholder="Write your C code here..."></textarea>
468+
</div>
426469
</div>
427470

428471
<div class="divider" id="divider"></div>
@@ -442,6 +485,7 @@ <h1>Null-Safe C Playground</h1>
442485

443486
<script>
444487
const editor = document.getElementById('editor');
488+
const editorHighlight = document.getElementById('editor-highlight');
445489
const output = document.getElementById('output');
446490
const compileBtn = document.getElementById('compileBtn');
447491
const status = document.getElementById('status');
@@ -453,6 +497,78 @@ <h1>Null-Safe C Playground</h1>
453497
const outputStats = document.getElementById('outputStats');
454498
const divider = document.getElementById('divider');
455499

500+
// Simple C syntax highlighter
501+
function highlightCode(code) {
502+
// Escape HTML
503+
code = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
504+
505+
// Store strings and comments temporarily to avoid highlighting inside them
506+
const protected = [];
507+
let index = 0;
508+
509+
// Protect multi-line comments
510+
code = code.replace(/\/\*[\s\S]*?\*\//g, match => {
511+
const placeholder = `___PROTECTED_${index}___`;
512+
protected.push(`<span class="hljs-comment">${match}</span>`);
513+
index++;
514+
return placeholder;
515+
});
516+
517+
// Protect single-line comments
518+
code = code.replace(/\/\/.*/g, match => {
519+
const placeholder = `___PROTECTED_${index}___`;
520+
protected.push(`<span class="hljs-comment">${match}</span>`);
521+
index++;
522+
return placeholder;
523+
});
524+
525+
// Protect strings
526+
code = code.replace(/"([^"\\]|\\.)*"/g, match => {
527+
const placeholder = `___PROTECTED_${index}___`;
528+
protected.push(`<span class="hljs-string">${match}</span>`);
529+
index++;
530+
return placeholder;
531+
});
532+
533+
// Now safely highlight everything else
534+
535+
// Preprocessor directives
536+
code = code.replace(/(^|\n)(#\s*(?:include|define|undef|ifdef|ifndef|if|else|elif|endif|pragma|error|warning|line)\b[^\n]*)/g,
537+
(match, newline, directive) => `${newline}<span class="hljs-preprocessor">${directive}</span>`);
538+
539+
// Numbers
540+
code = code.replace(/\b(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?[fFlLuU]?\b/g,
541+
match => `<span class="hljs-number">${match}</span>`);
542+
543+
// Keywords
544+
code = code.replace(/\b(auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|restrict|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while|_Bool|_Complex|_Imaginary|_Nullable|_Nonnull)\b/g,
545+
match => `<span class="hljs-keyword">${match}</span>`);
546+
547+
// Function names
548+
code = code.replace(/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/g,
549+
(match, name) => `<span class="hljs-function">${name}</span> (`);
550+
551+
// Restore protected strings and comments
552+
for (let i = 0; i < protected.length; i++) {
553+
code = code.replace(`___PROTECTED_${i}___`, protected[i]);
554+
}
555+
556+
return code;
557+
}
558+
559+
function updateHighlight() {
560+
const code = editor.value;
561+
editorHighlight.innerHTML = highlightCode(code);
562+
// Sync scroll
563+
editorHighlight.scrollTop = editor.scrollTop;
564+
}
565+
566+
// Update highlighting on input
567+
editor.addEventListener('input', updateHighlight);
568+
editor.addEventListener('scroll', () => {
569+
editorHighlight.scrollTop = editor.scrollTop;
570+
});
571+
456572
const examples = {
457573
'null-check': `#include <stdio.h>
458574
#include <stdlib.h>
@@ -515,6 +631,7 @@ <h1>Null-Safe C Playground</h1>
515631

516632
const defaultCode = examples['null-check'];
517633
editor.value = defaultCode;
634+
updateHighlight(); // Initial highlight
518635

519636
let ClangModule = null;
520637
let isCompiling = false;
@@ -559,6 +676,7 @@ <h1>Null-Safe C Playground</h1>
559676
const example = e.target.value;
560677
if (example && examples[example]) {
561678
editor.value = examples[example];
679+
updateHighlight(); // Update highlighting
562680
showToast('Example loaded!');
563681
e.target.value = '';
564682
}

0 commit comments

Comments
 (0)