juxscript 1.1.47 → 1.1.49
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/lib/components/code.js +5 -5
- package/lib/components/code.ts +7 -7
- package/lib/utils/codeparser.d.ts +9 -46
- package/lib/utils/codeparser.d.ts.map +1 -1
- package/lib/utils/codeparser.js +111 -222
- package/lib/utils/codeparser.ts +117 -264
- package/package.json +1 -1
package/lib/components/code.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
-
import {
|
|
2
|
+
import { parseCode, renderLineWithTokens, getSyntaxHighlightCSS } from '../utils/codeparser.js';
|
|
3
3
|
// Event definitions
|
|
4
4
|
const TRIGGER_EVENTS = [];
|
|
5
5
|
const CALLBACK_EVENTS = [];
|
|
@@ -68,7 +68,7 @@ export class Code extends BaseComponent {
|
|
|
68
68
|
* Parse code content into individual lines with tokens
|
|
69
69
|
*/
|
|
70
70
|
_parseLines() {
|
|
71
|
-
return
|
|
71
|
+
return parseCode(this.state.content, this.state.language);
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Rebuild all line elements
|
|
@@ -81,7 +81,7 @@ export class Code extends BaseComponent {
|
|
|
81
81
|
return;
|
|
82
82
|
codeEl.innerHTML = '';
|
|
83
83
|
const parsedLines = this._parseLines();
|
|
84
|
-
const { startLine, highlightLines
|
|
84
|
+
const { startLine, highlightLines } = this.state;
|
|
85
85
|
parsedLines.forEach((parsedLine, index) => {
|
|
86
86
|
const lineNumber = startLine + index;
|
|
87
87
|
const isHighlighted = highlightLines.includes(lineNumber);
|
|
@@ -109,7 +109,7 @@ export class Code extends BaseComponent {
|
|
|
109
109
|
// Line content with syntax highlighting
|
|
110
110
|
const lineCode = document.createElement('span');
|
|
111
111
|
lineCode.className = 'jux-code-line-content';
|
|
112
|
-
lineCode.innerHTML =
|
|
112
|
+
lineCode.innerHTML = renderLineWithTokens(parsedLine);
|
|
113
113
|
lineEl.appendChild(lineCode);
|
|
114
114
|
return lineEl;
|
|
115
115
|
}
|
|
@@ -135,7 +135,7 @@ export class Code extends BaseComponent {
|
|
|
135
135
|
if (!Code._syntaxCSSInjected) {
|
|
136
136
|
const style = document.createElement('style');
|
|
137
137
|
style.id = 'jux-code-syntax-css';
|
|
138
|
-
style.textContent =
|
|
138
|
+
style.textContent = getSyntaxHighlightCSS();
|
|
139
139
|
document.head.appendChild(style);
|
|
140
140
|
Code._syntaxCSSInjected = true;
|
|
141
141
|
}
|
package/lib/components/code.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
-
import {
|
|
2
|
+
import { parseCode, renderLineWithTokens, getSyntaxHighlightCSS } from '../utils/codeparser.js';
|
|
3
3
|
|
|
4
4
|
// Event definitions
|
|
5
5
|
const TRIGGER_EVENTS = [] as const;
|
|
@@ -103,8 +103,8 @@ export class Code extends BaseComponent<CodeState> {
|
|
|
103
103
|
/**
|
|
104
104
|
* Parse code content into individual lines with tokens
|
|
105
105
|
*/
|
|
106
|
-
private _parseLines(): ReturnType<typeof
|
|
107
|
-
return
|
|
106
|
+
private _parseLines(): ReturnType<typeof parseCode> {
|
|
107
|
+
return parseCode(this.state.content, this.state.language);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/**
|
|
@@ -119,7 +119,7 @@ export class Code extends BaseComponent<CodeState> {
|
|
|
119
119
|
codeEl.innerHTML = '';
|
|
120
120
|
|
|
121
121
|
const parsedLines = this._parseLines();
|
|
122
|
-
const { startLine, highlightLines
|
|
122
|
+
const { startLine, highlightLines } = this.state;
|
|
123
123
|
|
|
124
124
|
parsedLines.forEach((parsedLine, index) => {
|
|
125
125
|
const lineNumber = startLine + index;
|
|
@@ -133,7 +133,7 @@ export class Code extends BaseComponent<CodeState> {
|
|
|
133
133
|
/**
|
|
134
134
|
* Create a single line element with parsed tokens
|
|
135
135
|
*/
|
|
136
|
-
private _createLineElement(parsedLine: ReturnType<typeof
|
|
136
|
+
private _createLineElement(parsedLine: ReturnType<typeof parseCode>[0], lineNumber: number, highlighted: boolean): HTMLElement {
|
|
137
137
|
const lineEl = document.createElement('div');
|
|
138
138
|
lineEl.className = 'jux-code-line';
|
|
139
139
|
lineEl.setAttribute('data-line', String(lineNumber));
|
|
@@ -153,7 +153,7 @@ export class Code extends BaseComponent<CodeState> {
|
|
|
153
153
|
// Line content with syntax highlighting
|
|
154
154
|
const lineCode = document.createElement('span');
|
|
155
155
|
lineCode.className = 'jux-code-line-content';
|
|
156
|
-
lineCode.innerHTML =
|
|
156
|
+
lineCode.innerHTML = renderLineWithTokens(parsedLine);
|
|
157
157
|
lineEl.appendChild(lineCode);
|
|
158
158
|
|
|
159
159
|
return lineEl;
|
|
@@ -184,7 +184,7 @@ export class Code extends BaseComponent<CodeState> {
|
|
|
184
184
|
if (!Code._syntaxCSSInjected) {
|
|
185
185
|
const style = document.createElement('style');
|
|
186
186
|
style.id = 'jux-code-syntax-css';
|
|
187
|
-
style.textContent =
|
|
187
|
+
style.textContent = getSyntaxHighlightCSS();
|
|
188
188
|
document.head.appendChild(style);
|
|
189
189
|
Code._syntaxCSSInjected = true;
|
|
190
190
|
}
|
|
@@ -1,69 +1,32 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Token type to CSS class mapping
|
|
3
|
-
*/
|
|
4
|
-
export declare const TOKEN_CLASS_MAP: Record<string, string>;
|
|
5
|
-
export interface ParsedToken {
|
|
6
|
-
type: string;
|
|
7
|
-
value: string;
|
|
8
|
-
start: number;
|
|
9
|
-
end: number;
|
|
10
|
-
line: number;
|
|
11
|
-
column: number;
|
|
12
|
-
className: string;
|
|
13
|
-
}
|
|
14
1
|
export interface ParsedLine {
|
|
15
2
|
lineNumber: number;
|
|
16
|
-
|
|
3
|
+
html: string;
|
|
17
4
|
raw: string;
|
|
18
5
|
}
|
|
19
|
-
export declare class CodeParser {
|
|
20
|
-
private static _cache;
|
|
21
|
-
/**
|
|
22
|
-
* Parse code with caching
|
|
23
|
-
*/
|
|
24
|
-
static parse(code: string, language?: string): ParsedLine[];
|
|
25
|
-
private static _parseCode;
|
|
26
|
-
/**
|
|
27
|
-
* ✅ NEW: Strip TypeScript type annotations for parsing
|
|
28
|
-
* This is a simple regex-based approach - not perfect but works for most cases
|
|
29
|
-
*/
|
|
30
|
-
private static _stripTypeScriptTypes;
|
|
31
|
-
/**
|
|
32
|
-
* Clear cache (useful for development)
|
|
33
|
-
*/
|
|
34
|
-
static clearCache(): void;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Get CSS class for a token
|
|
38
|
-
*/
|
|
39
|
-
export declare function getTokenClass(token: any): string;
|
|
40
6
|
/**
|
|
41
|
-
*
|
|
7
|
+
* Parse code into lines with simple syntax highlighting
|
|
42
8
|
*/
|
|
43
|
-
export declare function
|
|
9
|
+
export declare function parseCode(code: string, language?: string): ParsedLine[];
|
|
44
10
|
/**
|
|
45
|
-
* Render a parsed line
|
|
11
|
+
* Render a parsed line (just returns the pre-generated HTML)
|
|
46
12
|
*/
|
|
47
13
|
export declare function renderLineWithTokens(parsedLine: ParsedLine): string;
|
|
48
14
|
/**
|
|
49
15
|
* Escape HTML entities
|
|
50
16
|
*/
|
|
51
|
-
|
|
17
|
+
declare function escapeHtml(text: string): string;
|
|
52
18
|
/**
|
|
53
19
|
* Generate CSS for syntax highlighting
|
|
54
20
|
*/
|
|
55
21
|
export declare function getSyntaxHighlightCSS(): string;
|
|
56
22
|
/**
|
|
57
|
-
* Main parser export
|
|
23
|
+
* Main parser export
|
|
58
24
|
*/
|
|
59
|
-
|
|
60
|
-
parse: typeof
|
|
25
|
+
declare const _default: {
|
|
26
|
+
parse: typeof parseCode;
|
|
61
27
|
renderLine: typeof renderLineWithTokens;
|
|
62
28
|
getCSS: typeof getSyntaxHighlightCSS;
|
|
63
|
-
getTokenClass: typeof getTokenClass;
|
|
64
|
-
isKeyword: typeof isKeyword;
|
|
65
29
|
escapeHtml: typeof escapeHtml;
|
|
66
|
-
TOKEN_CLASS_MAP: Record<string, string>;
|
|
67
30
|
};
|
|
68
|
-
export default
|
|
31
|
+
export default _default;
|
|
69
32
|
//# sourceMappingURL=codeparser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codeparser.d.ts","sourceRoot":"","sources":["codeparser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codeparser.d.ts","sourceRoot":"","sources":["codeparser.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAqB,GAAG,UAAU,EAAE,CAQrF;AAqCD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAsE9C;AAED;;GAEG;;;;;;;AACH,wBAKE"}
|
package/lib/utils/codeparser.js
CHANGED
|
@@ -1,220 +1,62 @@
|
|
|
1
|
-
import * as acorn from 'acorn';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
2
|
+
* Simple keyword lists for highlighting
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
|
|
13
|
-
'return': 'token-keyword',
|
|
14
|
-
'if': 'token-keyword',
|
|
15
|
-
'else': 'token-keyword',
|
|
16
|
-
'for': 'token-keyword',
|
|
17
|
-
'while': 'token-keyword',
|
|
18
|
-
'class': 'token-keyword',
|
|
19
|
-
'import': 'token-keyword',
|
|
20
|
-
'export': 'token-keyword',
|
|
21
|
-
'from': 'token-keyword',
|
|
22
|
-
'async': 'token-keyword',
|
|
23
|
-
'await': 'token-keyword',
|
|
24
|
-
// Literals
|
|
25
|
-
'String': 'token-string',
|
|
26
|
-
'Numeric': 'token-number',
|
|
27
|
-
'Boolean': 'token-boolean',
|
|
28
|
-
'Null': 'token-null',
|
|
29
|
-
'RegExp': 'token-regex',
|
|
30
|
-
// Identifiers
|
|
31
|
-
'Identifier': 'token-identifier',
|
|
32
|
-
'PrivateIdentifier': 'token-identifier',
|
|
33
|
-
// Operators
|
|
34
|
-
'Punctuator': 'token-punctuation',
|
|
35
|
-
// Comments
|
|
36
|
-
'LineComment': 'token-comment',
|
|
37
|
-
'BlockComment': 'token-comment',
|
|
38
|
-
};
|
|
39
|
-
export class CodeParser {
|
|
40
|
-
/**
|
|
41
|
-
* Parse code with caching
|
|
42
|
-
*/
|
|
43
|
-
static parse(code, language = 'javascript') {
|
|
44
|
-
const cacheKey = `${language}:${code}`;
|
|
45
|
-
if (this._cache.has(cacheKey)) {
|
|
46
|
-
return this._cache.get(cacheKey);
|
|
47
|
-
}
|
|
48
|
-
const result = this._parseCode(code, language);
|
|
49
|
-
this._cache.set(cacheKey, result);
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
static _parseCode(code, language) {
|
|
53
|
-
const lines = [];
|
|
54
|
-
const codeLines = code.split('\n');
|
|
55
|
-
// ✅ Skip parsing for non-JS languages
|
|
56
|
-
if (!['javascript', 'typescript', 'js', 'ts', 'jsx', 'tsx'].includes(language.toLowerCase())) {
|
|
57
|
-
codeLines.forEach((lineText, index) => {
|
|
58
|
-
lines.push({
|
|
59
|
-
lineNumber: index + 1,
|
|
60
|
-
tokens: [],
|
|
61
|
-
raw: lineText
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
return lines;
|
|
65
|
-
}
|
|
66
|
-
// ✅ Strip TypeScript types for parsing
|
|
67
|
-
const cleanedCode = language.toLowerCase().includes('typescript') || language.toLowerCase().includes('ts')
|
|
68
|
-
? this._stripTypeScriptTypes(code)
|
|
69
|
-
: code;
|
|
70
|
-
try {
|
|
71
|
-
// Parse with Acorn (supports ES2020+)
|
|
72
|
-
const tokens = [];
|
|
73
|
-
acorn.parse(cleanedCode, {
|
|
74
|
-
ecmaVersion: 2022,
|
|
75
|
-
sourceType: 'module',
|
|
76
|
-
locations: true,
|
|
77
|
-
ranges: true,
|
|
78
|
-
onToken: tokens,
|
|
79
|
-
onComment: (block, text, start, end, startLoc, endLoc) => {
|
|
80
|
-
tokens.push({
|
|
81
|
-
type: block ? 'BlockComment' : 'LineComment',
|
|
82
|
-
value: text,
|
|
83
|
-
start,
|
|
84
|
-
end,
|
|
85
|
-
loc: { start: startLoc, end: endLoc }
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
// Sort tokens by position
|
|
90
|
-
tokens.sort((a, b) => a.start - b.start);
|
|
91
|
-
// Group tokens by line
|
|
92
|
-
const tokensByLine = new Map();
|
|
93
|
-
tokens.forEach(token => {
|
|
94
|
-
const line = token.loc.start.line;
|
|
95
|
-
const parsedToken = {
|
|
96
|
-
type: token.type.label || token.type,
|
|
97
|
-
value: token.value !== undefined ? String(token.value) : cleanedCode.substring(token.start, token.end),
|
|
98
|
-
start: token.start,
|
|
99
|
-
end: token.end,
|
|
100
|
-
line: token.loc.start.line,
|
|
101
|
-
column: token.loc.start.column,
|
|
102
|
-
className: getTokenClass(token)
|
|
103
|
-
};
|
|
104
|
-
if (!tokensByLine.has(line)) {
|
|
105
|
-
tokensByLine.set(line, []);
|
|
106
|
-
}
|
|
107
|
-
tokensByLine.get(line).push(parsedToken);
|
|
108
|
-
});
|
|
109
|
-
// ✅ Build lines with tokens (use ORIGINAL code, not cleaned)
|
|
110
|
-
codeLines.forEach((lineText, index) => {
|
|
111
|
-
const lineNumber = index + 1;
|
|
112
|
-
const lineTokens = tokensByLine.get(lineNumber) || [];
|
|
113
|
-
lines.push({
|
|
114
|
-
lineNumber,
|
|
115
|
-
tokens: lineTokens,
|
|
116
|
-
raw: lineText // Use original line with types
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
// Fallback: if parsing fails, return plain lines
|
|
122
|
-
console.warn('[CodeParser] Parse failed, using plain text:', error);
|
|
123
|
-
codeLines.forEach((lineText, index) => {
|
|
124
|
-
lines.push({
|
|
125
|
-
lineNumber: index + 1,
|
|
126
|
-
tokens: [],
|
|
127
|
-
raw: lineText
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
return lines;
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* ✅ NEW: Strip TypeScript type annotations for parsing
|
|
135
|
-
* This is a simple regex-based approach - not perfect but works for most cases
|
|
136
|
-
*/
|
|
137
|
-
static _stripTypeScriptTypes(code) {
|
|
138
|
-
return code
|
|
139
|
-
// Remove type annotations from parameters: (name: string) -> (name)
|
|
140
|
-
.replace(/(\w+)\s*:\s*[\w<>\[\]|&]+/g, '$1')
|
|
141
|
-
// Remove return type annotations: ): string -> )
|
|
142
|
-
.replace(/\)\s*:\s*[\w<>\[\]|&]+/g, ')')
|
|
143
|
-
// Remove interface/type declarations (keep as comments to preserve line numbers)
|
|
144
|
-
.replace(/^(\s*)(interface|type)\s+\w+.*$/gm, '$1// $2 declaration')
|
|
145
|
-
// Remove as type assertions: value as string -> value
|
|
146
|
-
.replace(/\s+as\s+[\w<>\[\]|&]+/g, '')
|
|
147
|
-
// Remove angle bracket generics: Array<string> -> Array
|
|
148
|
-
.replace(/<[\w<>\[\]|&,\s]+>/g, '');
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Clear cache (useful for development)
|
|
152
|
-
*/
|
|
153
|
-
static clearCache() {
|
|
154
|
-
this._cache.clear();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
CodeParser._cache = new Map();
|
|
4
|
+
const KEYWORDS = new Set([
|
|
5
|
+
'async', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue',
|
|
6
|
+
'debugger', 'default', 'delete', 'do', 'else', 'export', 'extends',
|
|
7
|
+
'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof',
|
|
8
|
+
'let', 'new', 'return', 'super', 'switch', 'this', 'throw', 'try',
|
|
9
|
+
'typeof', 'var', 'void', 'while', 'with', 'yield', 'from', 'of',
|
|
10
|
+
'static', 'get', 'set', 'as', 'interface', 'type', 'enum', 'namespace'
|
|
11
|
+
]);
|
|
158
12
|
/**
|
|
159
|
-
*
|
|
13
|
+
* Parse code into lines with simple syntax highlighting
|
|
160
14
|
*/
|
|
161
|
-
export function
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return TOKEN_CLASS_MAP[type] || 'token-default';
|
|
15
|
+
export function parseCode(code, language = 'javascript') {
|
|
16
|
+
const lines = code.split('\n');
|
|
17
|
+
return lines.map((line, index) => ({
|
|
18
|
+
lineNumber: index + 1,
|
|
19
|
+
html: highlightLine(line),
|
|
20
|
+
raw: line
|
|
21
|
+
}));
|
|
169
22
|
}
|
|
170
23
|
/**
|
|
171
|
-
*
|
|
24
|
+
* Highlight a single line of code
|
|
172
25
|
*/
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
26
|
+
function highlightLine(line) {
|
|
27
|
+
if (!line.trim())
|
|
28
|
+
return ' ';
|
|
29
|
+
let result = line;
|
|
30
|
+
// 1. Comments (do first to avoid highlighting keywords in comments)
|
|
31
|
+
result = result.replace(/(\/\/.*$)/g, '<span class="token-comment">$1</span>');
|
|
32
|
+
result = result.replace(/(\/\*[\s\S]*?\*\/)/g, '<span class="token-comment">$1</span>');
|
|
33
|
+
// 2. Strings (avoid highlighting keywords in strings)
|
|
34
|
+
result = result.replace(/(['"`])(?:(?=(\\?))\2.)*?\1/g, (match) => {
|
|
35
|
+
return `<span class="token-string">${escapeHtml(match)}</span>`;
|
|
36
|
+
});
|
|
37
|
+
// 3. Numbers
|
|
38
|
+
result = result.replace(/\b(\d+\.?\d*)\b/g, '<span class="token-number">$1</span>');
|
|
39
|
+
// 4. Keywords (only whole words)
|
|
40
|
+
KEYWORDS.forEach(keyword => {
|
|
41
|
+
const regex = new RegExp(`\\b(${keyword})\\b`, 'g');
|
|
42
|
+
result = result.replace(regex, '<span class="token-keyword">$1</span>');
|
|
43
|
+
});
|
|
44
|
+
// 5. Braces, brackets, parentheses
|
|
45
|
+
result = result.replace(/([{}[\]()])/g, '<span class="token-punctuation">$1</span>');
|
|
46
|
+
// 6. Operators
|
|
47
|
+
result = result.replace(/([+\-*/%=<>!&|^~?:])/g, '<span class="token-operator">$1</span>');
|
|
48
|
+
return result || ' ';
|
|
182
49
|
}
|
|
183
50
|
/**
|
|
184
|
-
* Render a parsed line
|
|
51
|
+
* Render a parsed line (just returns the pre-generated HTML)
|
|
185
52
|
*/
|
|
186
53
|
export function renderLineWithTokens(parsedLine) {
|
|
187
|
-
|
|
188
|
-
// No tokens, return plain text (escaped)
|
|
189
|
-
return escapeHtml(parsedLine.raw) || ' ';
|
|
190
|
-
}
|
|
191
|
-
const { raw, tokens } = parsedLine;
|
|
192
|
-
let html = '';
|
|
193
|
-
let lastIndex = 0;
|
|
194
|
-
// Calculate line start position in original code
|
|
195
|
-
const lineStartPos = tokens[0]?.start - tokens[0]?.column;
|
|
196
|
-
tokens.forEach(token => {
|
|
197
|
-
// Add any whitespace/text between tokens
|
|
198
|
-
const localStart = token.start - lineStartPos;
|
|
199
|
-
if (localStart > lastIndex) {
|
|
200
|
-
html += escapeHtml(raw.substring(lastIndex, localStart));
|
|
201
|
-
}
|
|
202
|
-
// Add token with span
|
|
203
|
-
const localEnd = token.end - lineStartPos;
|
|
204
|
-
const tokenText = raw.substring(localStart, localEnd);
|
|
205
|
-
html += `<span class="${token.className}">${escapeHtml(tokenText)}</span>`;
|
|
206
|
-
lastIndex = localEnd;
|
|
207
|
-
});
|
|
208
|
-
// Add any remaining text
|
|
209
|
-
if (lastIndex < raw.length) {
|
|
210
|
-
html += escapeHtml(raw.substring(lastIndex));
|
|
211
|
-
}
|
|
212
|
-
return html || ' ';
|
|
54
|
+
return parsedLine.html;
|
|
213
55
|
}
|
|
214
56
|
/**
|
|
215
57
|
* Escape HTML entities
|
|
216
58
|
*/
|
|
217
|
-
|
|
59
|
+
function escapeHtml(text) {
|
|
218
60
|
return text
|
|
219
61
|
.replace(/&/g, '&')
|
|
220
62
|
.replace(/</g, '<')
|
|
@@ -227,34 +69,81 @@ export function escapeHtml(text) {
|
|
|
227
69
|
*/
|
|
228
70
|
export function getSyntaxHighlightCSS() {
|
|
229
71
|
return `
|
|
230
|
-
/*
|
|
72
|
+
/* Simple Syntax Highlighting */
|
|
73
|
+
.jux-code {
|
|
74
|
+
font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
|
|
75
|
+
font-size: 14px;
|
|
76
|
+
line-height: 1.6;
|
|
77
|
+
background: #1e1e1e;
|
|
78
|
+
border-radius: 8px;
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
margin: 1rem 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.jux-code pre {
|
|
84
|
+
margin: 0;
|
|
85
|
+
padding: 1rem;
|
|
86
|
+
overflow-x: auto;
|
|
87
|
+
background: transparent;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.jux-code-lines {
|
|
91
|
+
display: block;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.jux-code-line {
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: flex-start;
|
|
97
|
+
min-height: 1.6em;
|
|
98
|
+
transition: background-color 0.2s;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.jux-code-line:hover {
|
|
102
|
+
background-color: rgba(255, 255, 255, 0.05);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.jux-code-line-highlight {
|
|
106
|
+
background-color: rgba(255, 215, 0, 0.1);
|
|
107
|
+
border-left: 3px solid #ffd700;
|
|
108
|
+
padding-left: 0.5rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.jux-code-line-number {
|
|
112
|
+
display: inline-block;
|
|
113
|
+
min-width: 3rem;
|
|
114
|
+
text-align: right;
|
|
115
|
+
padding-right: 1rem;
|
|
116
|
+
color: #858585;
|
|
117
|
+
user-select: none;
|
|
118
|
+
flex-shrink: 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.jux-code-line-content {
|
|
122
|
+
flex: 1;
|
|
123
|
+
color: #d4d4d4;
|
|
124
|
+
white-space: pre;
|
|
125
|
+
overflow-x: auto;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.jux-code-no-numbers .jux-code-line-number {
|
|
129
|
+
display: none;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Token colors */
|
|
231
133
|
.token-keyword { color: #c678dd; font-weight: 600; }
|
|
232
134
|
.token-string { color: #98c379; }
|
|
233
135
|
.token-number { color: #d19a66; }
|
|
234
|
-
.token-boolean { color: #d19a66; }
|
|
235
|
-
.token-null { color: #d19a66; }
|
|
236
|
-
.token-regex { color: #e06c75; }
|
|
237
|
-
.token-identifier { color: #e5c07b; }
|
|
238
|
-
.token-punctuation { color: #abb2bf; }
|
|
239
136
|
.token-comment { color: #5c6370; font-style: italic; }
|
|
240
|
-
.token-
|
|
241
|
-
|
|
242
|
-
/* Function calls */
|
|
243
|
-
.token-identifier + .token-punctuation:has(+ .token-punctuation) {
|
|
244
|
-
color: #61afef; /* Function names in blue */
|
|
245
|
-
}
|
|
137
|
+
.token-punctuation { color: #abb2bf; }
|
|
138
|
+
.token-operator { color: #56b6c2; }
|
|
246
139
|
`;
|
|
247
140
|
}
|
|
248
141
|
/**
|
|
249
|
-
* Main parser export
|
|
142
|
+
* Main parser export
|
|
250
143
|
*/
|
|
251
|
-
export
|
|
252
|
-
parse:
|
|
144
|
+
export default {
|
|
145
|
+
parse: parseCode,
|
|
253
146
|
renderLine: renderLineWithTokens,
|
|
254
147
|
getCSS: getSyntaxHighlightCSS,
|
|
255
|
-
|
|
256
|
-
isKeyword,
|
|
257
|
-
escapeHtml,
|
|
258
|
-
TOKEN_CLASS_MAP
|
|
148
|
+
escapeHtml
|
|
259
149
|
};
|
|
260
|
-
export default CodeParser;
|
package/lib/utils/codeparser.ts
CHANGED
|
@@ -1,271 +1,80 @@
|
|
|
1
|
-
import * as acorn from 'acorn';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
2
|
+
* Simple keyword lists for highlighting
|
|
5
3
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
|
|
14
|
-
'return': 'token-keyword',
|
|
15
|
-
'if': 'token-keyword',
|
|
16
|
-
'else': 'token-keyword',
|
|
17
|
-
'for': 'token-keyword',
|
|
18
|
-
'while': 'token-keyword',
|
|
19
|
-
'class': 'token-keyword',
|
|
20
|
-
'import': 'token-keyword',
|
|
21
|
-
'export': 'token-keyword',
|
|
22
|
-
'from': 'token-keyword',
|
|
23
|
-
'async': 'token-keyword',
|
|
24
|
-
'await': 'token-keyword',
|
|
25
|
-
|
|
26
|
-
// Literals
|
|
27
|
-
'String': 'token-string',
|
|
28
|
-
'Numeric': 'token-number',
|
|
29
|
-
'Boolean': 'token-boolean',
|
|
30
|
-
'Null': 'token-null',
|
|
31
|
-
'RegExp': 'token-regex',
|
|
32
|
-
|
|
33
|
-
// Identifiers
|
|
34
|
-
'Identifier': 'token-identifier',
|
|
35
|
-
'PrivateIdentifier': 'token-identifier',
|
|
36
|
-
|
|
37
|
-
// Operators
|
|
38
|
-
'Punctuator': 'token-punctuation',
|
|
39
|
-
|
|
40
|
-
// Comments
|
|
41
|
-
'LineComment': 'token-comment',
|
|
42
|
-
'BlockComment': 'token-comment',
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export interface ParsedToken {
|
|
46
|
-
type: string;
|
|
47
|
-
value: string;
|
|
48
|
-
start: number;
|
|
49
|
-
end: number;
|
|
50
|
-
line: number;
|
|
51
|
-
column: number;
|
|
52
|
-
className: string;
|
|
53
|
-
}
|
|
4
|
+
const KEYWORDS = new Set([
|
|
5
|
+
'async', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue',
|
|
6
|
+
'debugger', 'default', 'delete', 'do', 'else', 'export', 'extends',
|
|
7
|
+
'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof',
|
|
8
|
+
'let', 'new', 'return', 'super', 'switch', 'this', 'throw', 'try',
|
|
9
|
+
'typeof', 'var', 'void', 'while', 'with', 'yield', 'from', 'of',
|
|
10
|
+
'static', 'get', 'set', 'as', 'interface', 'type', 'enum', 'namespace'
|
|
11
|
+
]);
|
|
54
12
|
|
|
55
13
|
export interface ParsedLine {
|
|
56
14
|
lineNumber: number;
|
|
57
|
-
|
|
15
|
+
html: string;
|
|
58
16
|
raw: string;
|
|
59
17
|
}
|
|
60
18
|
|
|
61
|
-
export class CodeParser {
|
|
62
|
-
private static _cache = new Map<string, ParsedLine[]>();
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Parse code with caching
|
|
66
|
-
*/
|
|
67
|
-
static parse(code: string, language: string = 'javascript'): ParsedLine[] {
|
|
68
|
-
const cacheKey = `${language}:${code}`;
|
|
69
|
-
|
|
70
|
-
if (this._cache.has(cacheKey)) {
|
|
71
|
-
return this._cache.get(cacheKey)!;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const result = this._parseCode(code, language);
|
|
75
|
-
this._cache.set(cacheKey, result);
|
|
76
|
-
return result;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private static _parseCode(code: string, language: string): ParsedLine[] {
|
|
80
|
-
const lines: ParsedLine[] = [];
|
|
81
|
-
const codeLines = code.split('\n');
|
|
82
|
-
|
|
83
|
-
// ✅ Skip parsing for non-JS languages
|
|
84
|
-
if (!['javascript', 'typescript', 'js', 'ts', 'jsx', 'tsx'].includes(language.toLowerCase())) {
|
|
85
|
-
codeLines.forEach((lineText, index) => {
|
|
86
|
-
lines.push({
|
|
87
|
-
lineNumber: index + 1,
|
|
88
|
-
tokens: [],
|
|
89
|
-
raw: lineText
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
return lines;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ✅ Strip TypeScript types for parsing
|
|
96
|
-
const cleanedCode = language.toLowerCase().includes('typescript') || language.toLowerCase().includes('ts')
|
|
97
|
-
? this._stripTypeScriptTypes(code)
|
|
98
|
-
: code;
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
// Parse with Acorn (supports ES2020+)
|
|
102
|
-
const tokens: any[] = [];
|
|
103
|
-
|
|
104
|
-
acorn.parse(cleanedCode, {
|
|
105
|
-
ecmaVersion: 2022,
|
|
106
|
-
sourceType: 'module',
|
|
107
|
-
locations: true,
|
|
108
|
-
ranges: true,
|
|
109
|
-
onToken: tokens,
|
|
110
|
-
onComment: (block, text, start, end, startLoc, endLoc) => {
|
|
111
|
-
tokens.push({
|
|
112
|
-
type: block ? 'BlockComment' : 'LineComment',
|
|
113
|
-
value: text,
|
|
114
|
-
start,
|
|
115
|
-
end,
|
|
116
|
-
loc: { start: startLoc, end: endLoc }
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// Sort tokens by position
|
|
122
|
-
tokens.sort((a, b) => a.start - b.start);
|
|
123
|
-
|
|
124
|
-
// Group tokens by line
|
|
125
|
-
const tokensByLine: Map<number, ParsedToken[]> = new Map();
|
|
126
|
-
|
|
127
|
-
tokens.forEach(token => {
|
|
128
|
-
const line = token.loc.start.line;
|
|
129
|
-
const parsedToken: ParsedToken = {
|
|
130
|
-
type: token.type.label || token.type,
|
|
131
|
-
value: token.value !== undefined ? String(token.value) : cleanedCode.substring(token.start, token.end),
|
|
132
|
-
start: token.start,
|
|
133
|
-
end: token.end,
|
|
134
|
-
line: token.loc.start.line,
|
|
135
|
-
column: token.loc.start.column,
|
|
136
|
-
className: getTokenClass(token)
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
if (!tokensByLine.has(line)) {
|
|
140
|
-
tokensByLine.set(line, []);
|
|
141
|
-
}
|
|
142
|
-
tokensByLine.get(line)!.push(parsedToken);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// ✅ Build lines with tokens (use ORIGINAL code, not cleaned)
|
|
146
|
-
codeLines.forEach((lineText, index) => {
|
|
147
|
-
const lineNumber = index + 1;
|
|
148
|
-
const lineTokens = tokensByLine.get(lineNumber) || [];
|
|
149
|
-
|
|
150
|
-
lines.push({
|
|
151
|
-
lineNumber,
|
|
152
|
-
tokens: lineTokens,
|
|
153
|
-
raw: lineText // Use original line with types
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
} catch (error) {
|
|
158
|
-
// Fallback: if parsing fails, return plain lines
|
|
159
|
-
console.warn('[CodeParser] Parse failed, using plain text:', error);
|
|
160
|
-
codeLines.forEach((lineText, index) => {
|
|
161
|
-
lines.push({
|
|
162
|
-
lineNumber: index + 1,
|
|
163
|
-
tokens: [],
|
|
164
|
-
raw: lineText
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return lines;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* ✅ NEW: Strip TypeScript type annotations for parsing
|
|
174
|
-
* This is a simple regex-based approach - not perfect but works for most cases
|
|
175
|
-
*/
|
|
176
|
-
private static _stripTypeScriptTypes(code: string): string {
|
|
177
|
-
return code
|
|
178
|
-
// Remove type annotations from parameters: (name: string) -> (name)
|
|
179
|
-
.replace(/(\w+)\s*:\s*[\w<>\[\]|&]+/g, '$1')
|
|
180
|
-
// Remove return type annotations: ): string -> )
|
|
181
|
-
.replace(/\)\s*:\s*[\w<>\[\]|&]+/g, ')')
|
|
182
|
-
// Remove interface/type declarations (keep as comments to preserve line numbers)
|
|
183
|
-
.replace(/^(\s*)(interface|type)\s+\w+.*$/gm, '$1// $2 declaration')
|
|
184
|
-
// Remove as type assertions: value as string -> value
|
|
185
|
-
.replace(/\s+as\s+[\w<>\[\]|&]+/g, '')
|
|
186
|
-
// Remove angle bracket generics: Array<string> -> Array
|
|
187
|
-
.replace(/<[\w<>\[\]|&,\s]+>/g, '');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Clear cache (useful for development)
|
|
192
|
-
*/
|
|
193
|
-
static clearCache(): void {
|
|
194
|
-
this._cache.clear();
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
19
|
/**
|
|
199
|
-
*
|
|
20
|
+
* Parse code into lines with simple syntax highlighting
|
|
200
21
|
*/
|
|
201
|
-
export function
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
// Map type to class
|
|
210
|
-
return TOKEN_CLASS_MAP[type] || 'token-default';
|
|
22
|
+
export function parseCode(code: string, language: string = 'javascript'): ParsedLine[] {
|
|
23
|
+
const lines = code.split('\n');
|
|
24
|
+
|
|
25
|
+
return lines.map((line, index) => ({
|
|
26
|
+
lineNumber: index + 1,
|
|
27
|
+
html: highlightLine(line),
|
|
28
|
+
raw: line
|
|
29
|
+
}));
|
|
211
30
|
}
|
|
212
31
|
|
|
213
32
|
/**
|
|
214
|
-
*
|
|
33
|
+
* Highlight a single line of code
|
|
215
34
|
*/
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger',
|
|
219
|
-
'default', 'delete', 'do', 'else', 'export', 'extends', 'finally', 'for',
|
|
220
|
-
'function', 'if', 'import', 'in', 'instanceof', 'let', 'new', 'return',
|
|
221
|
-
'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void',
|
|
222
|
-
'while', 'with', 'yield', 'async', 'of', 'static', 'get', 'set'
|
|
223
|
-
];
|
|
224
|
-
return keywords.includes(word);
|
|
225
|
-
}
|
|
35
|
+
function highlightLine(line: string): string {
|
|
36
|
+
if (!line.trim()) return ' ';
|
|
226
37
|
|
|
227
|
-
|
|
228
|
-
* Render a parsed line as HTML with spans
|
|
229
|
-
*/
|
|
230
|
-
export function renderLineWithTokens(parsedLine: ParsedLine): string {
|
|
231
|
-
if (parsedLine.tokens.length === 0) {
|
|
232
|
-
// No tokens, return plain text (escaped)
|
|
233
|
-
return escapeHtml(parsedLine.raw) || ' ';
|
|
234
|
-
}
|
|
38
|
+
let result = line;
|
|
235
39
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
40
|
+
// 1. Comments (do first to avoid highlighting keywords in comments)
|
|
41
|
+
result = result.replace(/(\/\/.*$)/g, '<span class="token-comment">$1</span>');
|
|
42
|
+
result = result.replace(/(\/\*[\s\S]*?\*\/)/g, '<span class="token-comment">$1</span>');
|
|
239
43
|
|
|
240
|
-
//
|
|
241
|
-
|
|
44
|
+
// 2. Strings (avoid highlighting keywords in strings)
|
|
45
|
+
result = result.replace(/(['"`])(?:(?=(\\?))\2.)*?\1/g, (match) => {
|
|
46
|
+
return `<span class="token-string">${escapeHtml(match)}</span>`;
|
|
47
|
+
});
|
|
242
48
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const localStart = token.start - lineStartPos;
|
|
246
|
-
if (localStart > lastIndex) {
|
|
247
|
-
html += escapeHtml(raw.substring(lastIndex, localStart));
|
|
248
|
-
}
|
|
49
|
+
// 3. Numbers
|
|
50
|
+
result = result.replace(/\b(\d+\.?\d*)\b/g, '<span class="token-number">$1</span>');
|
|
249
51
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
lastIndex = localEnd;
|
|
52
|
+
// 4. Keywords (only whole words)
|
|
53
|
+
KEYWORDS.forEach(keyword => {
|
|
54
|
+
const regex = new RegExp(`\\b(${keyword})\\b`, 'g');
|
|
55
|
+
result = result.replace(regex, '<span class="token-keyword">$1</span>');
|
|
255
56
|
});
|
|
256
57
|
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
58
|
+
// 5. Braces, brackets, parentheses
|
|
59
|
+
result = result.replace(/([{}[\]()])/g, '<span class="token-punctuation">$1</span>');
|
|
60
|
+
|
|
61
|
+
// 6. Operators
|
|
62
|
+
result = result.replace(/([+\-*/%=<>!&|^~?:])/g, '<span class="token-operator">$1</span>');
|
|
261
63
|
|
|
262
|
-
return
|
|
64
|
+
return result || ' ';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Render a parsed line (just returns the pre-generated HTML)
|
|
69
|
+
*/
|
|
70
|
+
export function renderLineWithTokens(parsedLine: ParsedLine): string {
|
|
71
|
+
return parsedLine.html;
|
|
263
72
|
}
|
|
264
73
|
|
|
265
74
|
/**
|
|
266
75
|
* Escape HTML entities
|
|
267
76
|
*/
|
|
268
|
-
|
|
77
|
+
function escapeHtml(text: string): string {
|
|
269
78
|
return text
|
|
270
79
|
.replace(/&/g, '&')
|
|
271
80
|
.replace(/</g, '<')
|
|
@@ -279,38 +88,82 @@ export function escapeHtml(text: string): string {
|
|
|
279
88
|
*/
|
|
280
89
|
export function getSyntaxHighlightCSS(): string {
|
|
281
90
|
return `
|
|
282
|
-
/*
|
|
91
|
+
/* Simple Syntax Highlighting */
|
|
92
|
+
.jux-code {
|
|
93
|
+
font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
|
|
94
|
+
font-size: 14px;
|
|
95
|
+
line-height: 1.6;
|
|
96
|
+
background: #1e1e1e;
|
|
97
|
+
border-radius: 8px;
|
|
98
|
+
overflow: hidden;
|
|
99
|
+
margin: 1rem 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.jux-code pre {
|
|
103
|
+
margin: 0;
|
|
104
|
+
padding: 1rem;
|
|
105
|
+
overflow-x: auto;
|
|
106
|
+
background: transparent;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.jux-code-lines {
|
|
110
|
+
display: block;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.jux-code-line {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: flex-start;
|
|
116
|
+
min-height: 1.6em;
|
|
117
|
+
transition: background-color 0.2s;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.jux-code-line:hover {
|
|
121
|
+
background-color: rgba(255, 255, 255, 0.05);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.jux-code-line-highlight {
|
|
125
|
+
background-color: rgba(255, 215, 0, 0.1);
|
|
126
|
+
border-left: 3px solid #ffd700;
|
|
127
|
+
padding-left: 0.5rem;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.jux-code-line-number {
|
|
131
|
+
display: inline-block;
|
|
132
|
+
min-width: 3rem;
|
|
133
|
+
text-align: right;
|
|
134
|
+
padding-right: 1rem;
|
|
135
|
+
color: #858585;
|
|
136
|
+
user-select: none;
|
|
137
|
+
flex-shrink: 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.jux-code-line-content {
|
|
141
|
+
flex: 1;
|
|
142
|
+
color: #d4d4d4;
|
|
143
|
+
white-space: pre;
|
|
144
|
+
overflow-x: auto;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.jux-code-no-numbers .jux-code-line-number {
|
|
148
|
+
display: none;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Token colors */
|
|
283
152
|
.token-keyword { color: #c678dd; font-weight: 600; }
|
|
284
153
|
.token-string { color: #98c379; }
|
|
285
154
|
.token-number { color: #d19a66; }
|
|
286
|
-
.token-boolean { color: #d19a66; }
|
|
287
|
-
.token-null { color: #d19a66; }
|
|
288
|
-
.token-regex { color: #e06c75; }
|
|
289
|
-
.token-identifier { color: #e5c07b; }
|
|
290
|
-
.token-punctuation { color: #abb2bf; }
|
|
291
155
|
.token-comment { color: #5c6370; font-style: italic; }
|
|
292
|
-
.token-
|
|
293
|
-
|
|
294
|
-
/* Function calls */
|
|
295
|
-
.token-identifier + .token-punctuation:has(+ .token-punctuation) {
|
|
296
|
-
color: #61afef; /* Function names in blue */
|
|
297
|
-
}
|
|
156
|
+
.token-punctuation { color: #abb2bf; }
|
|
157
|
+
.token-operator { color: #56b6c2; }
|
|
298
158
|
`;
|
|
299
159
|
}
|
|
300
160
|
|
|
301
|
-
|
|
302
|
-
|
|
303
161
|
/**
|
|
304
|
-
* Main parser export
|
|
162
|
+
* Main parser export
|
|
305
163
|
*/
|
|
306
|
-
export
|
|
307
|
-
parse:
|
|
164
|
+
export default {
|
|
165
|
+
parse: parseCode,
|
|
308
166
|
renderLine: renderLineWithTokens,
|
|
309
167
|
getCSS: getSyntaxHighlightCSS,
|
|
310
|
-
|
|
311
|
-
isKeyword,
|
|
312
|
-
escapeHtml,
|
|
313
|
-
TOKEN_CLASS_MAP
|
|
168
|
+
escapeHtml
|
|
314
169
|
};
|
|
315
|
-
|
|
316
|
-
export default CodeParser;
|