juxscript 1.1.56 → 1.1.57
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/utils/codeparser.d.ts +1 -1
- package/lib/utils/codeparser.d.ts.map +1 -1
- package/lib/utils/codeparser.js +71 -25
- package/lib/utils/codeparser.ts +76 -25
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ export interface ParsedLine {
|
|
|
8
8
|
*/
|
|
9
9
|
declare function escapeHtml(text: string): string;
|
|
10
10
|
/**
|
|
11
|
-
* Parse code into lines
|
|
11
|
+
* Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
|
|
12
12
|
*/
|
|
13
13
|
export declare function parseCode(code: string, language?: string): ParsedLine[];
|
|
14
14
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codeparser.d.ts","sourceRoot":"","sources":["codeparser.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAqB,GAAG,UAAU,EAAE,CAQrF;
|
|
1
|
+
{"version":3,"file":"codeparser.d.ts","sourceRoot":"","sources":["codeparser.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAqB,GAAG,UAAU,EAAE,CAQrF;AAuFD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAiE9C;;;;;;;AAED,wBAKE"}
|
package/lib/utils/codeparser.js
CHANGED
|
@@ -24,47 +24,90 @@ function escapeHtml(text) {
|
|
|
24
24
|
.replace(/'/g, ''');
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Parse code into lines
|
|
27
|
+
* Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
|
|
28
28
|
*/
|
|
29
29
|
export function parseCode(code, language = 'javascript') {
|
|
30
30
|
const lines = code.split('\n');
|
|
31
31
|
return lines.map((line, index) => ({
|
|
32
32
|
lineNumber: index + 1,
|
|
33
|
-
html:
|
|
33
|
+
html: tokenizeLine(line),
|
|
34
34
|
raw: line
|
|
35
35
|
}));
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Tokenize a single line character-by-character
|
|
39
39
|
*/
|
|
40
|
-
function
|
|
40
|
+
function tokenizeLine(line) {
|
|
41
41
|
if (!line.trim())
|
|
42
42
|
return ' ';
|
|
43
|
-
// ✅ Split line into tokens (words, operators, whitespace)
|
|
44
|
-
const tokens = line.match(/\w+|[^\w\s]|\s+/g) || [];
|
|
45
43
|
let result = '';
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// Whitespace - preserve
|
|
51
|
-
if (
|
|
52
|
-
result +=
|
|
53
|
-
|
|
44
|
+
let i = 0;
|
|
45
|
+
const len = line.length;
|
|
46
|
+
while (i < len) {
|
|
47
|
+
const char = line[i];
|
|
48
|
+
// Whitespace - preserve
|
|
49
|
+
if (/\s/.test(char)) {
|
|
50
|
+
result += char;
|
|
51
|
+
i++;
|
|
52
|
+
continue;
|
|
54
53
|
}
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
// Word (identifier or keyword)
|
|
55
|
+
if (/[a-zA-Z_$]/.test(char)) {
|
|
56
|
+
const word = consumeWord(line, i);
|
|
57
|
+
const escaped = escapeHtml(word);
|
|
58
|
+
if (KEYWORDS.has(word)) {
|
|
59
|
+
result += `<span class="token-keyword">${escaped}</span>`;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
result += escaped;
|
|
63
|
+
}
|
|
64
|
+
i += word.length;
|
|
65
|
+
continue;
|
|
60
66
|
}
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
// Number
|
|
68
|
+
if (/\d/.test(char)) {
|
|
69
|
+
const num = consumeNumber(line, i);
|
|
70
|
+
result += `<span class="token-number">${escapeHtml(num)}</span>`;
|
|
71
|
+
i += num.length;
|
|
72
|
+
continue;
|
|
64
73
|
}
|
|
65
|
-
|
|
74
|
+
// Everything else - just escape and append
|
|
75
|
+
result += escapeHtml(char);
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
66
78
|
return result || ' ';
|
|
67
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Consume a complete word (identifier)
|
|
82
|
+
*/
|
|
83
|
+
function consumeWord(line, start) {
|
|
84
|
+
let i = start;
|
|
85
|
+
while (i < line.length && /[a-zA-Z0-9_$]/.test(line[i])) {
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
return line.substring(start, i);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Consume a complete number
|
|
92
|
+
*/
|
|
93
|
+
function consumeNumber(line, start) {
|
|
94
|
+
let i = start;
|
|
95
|
+
let hasDot = false;
|
|
96
|
+
while (i < line.length) {
|
|
97
|
+
const char = line[i];
|
|
98
|
+
if (/\d/.test(char)) {
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
else if (char === '.' && !hasDot) {
|
|
102
|
+
hasDot = true;
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return line.substring(start, i);
|
|
110
|
+
}
|
|
68
111
|
/**
|
|
69
112
|
* Render a parsed line
|
|
70
113
|
*/
|
|
@@ -76,7 +119,7 @@ export function renderLineWithTokens(parsedLine) {
|
|
|
76
119
|
*/
|
|
77
120
|
export function getSyntaxHighlightCSS() {
|
|
78
121
|
return `
|
|
79
|
-
/* Simple
|
|
122
|
+
/* Simple Code Highlighting */
|
|
80
123
|
.jux-code {
|
|
81
124
|
font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
|
|
82
125
|
font-size: 14px;
|
|
@@ -130,10 +173,13 @@ export function getSyntaxHighlightCSS() {
|
|
|
130
173
|
display: none;
|
|
131
174
|
}
|
|
132
175
|
|
|
133
|
-
/* Token colors
|
|
176
|
+
/* Token colors */
|
|
134
177
|
.token-keyword {
|
|
135
178
|
color: #c678dd;
|
|
136
179
|
font-weight: 600;
|
|
180
|
+
}
|
|
181
|
+
.token-number {
|
|
182
|
+
color: #d19a66;
|
|
137
183
|
}
|
|
138
184
|
`;
|
|
139
185
|
}
|
package/lib/utils/codeparser.ts
CHANGED
|
@@ -32,55 +32,103 @@ function escapeHtml(text: string): string {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Parse code into lines
|
|
35
|
+
* Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
|
|
36
36
|
*/
|
|
37
37
|
export function parseCode(code: string, language: string = 'javascript'): ParsedLine[] {
|
|
38
38
|
const lines = code.split('\n');
|
|
39
39
|
|
|
40
40
|
return lines.map((line, index) => ({
|
|
41
41
|
lineNumber: index + 1,
|
|
42
|
-
html:
|
|
42
|
+
html: tokenizeLine(line),
|
|
43
43
|
raw: line
|
|
44
44
|
}));
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* Tokenize a single line character-by-character
|
|
49
49
|
*/
|
|
50
|
-
function
|
|
50
|
+
function tokenizeLine(line: string): string {
|
|
51
51
|
if (!line.trim()) return ' ';
|
|
52
52
|
|
|
53
|
-
// ✅ Split line into tokens (words, operators, whitespace)
|
|
54
|
-
const tokens = line.match(/\w+|[^\w\s]|\s+/g) || [];
|
|
55
|
-
|
|
56
53
|
let result = '';
|
|
54
|
+
let i = 0;
|
|
55
|
+
const len = line.length;
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (!token) return;
|
|
57
|
+
while (i < len) {
|
|
58
|
+
const char = line[i];
|
|
61
59
|
|
|
62
|
-
// Whitespace - preserve
|
|
63
|
-
if (
|
|
64
|
-
result +=
|
|
65
|
-
|
|
60
|
+
// Whitespace - preserve
|
|
61
|
+
if (/\s/.test(char)) {
|
|
62
|
+
result += char;
|
|
63
|
+
i++;
|
|
64
|
+
continue;
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
//
|
|
69
|
-
|
|
67
|
+
// Word (identifier or keyword)
|
|
68
|
+
if (/[a-zA-Z_$]/.test(char)) {
|
|
69
|
+
const word = consumeWord(line, i);
|
|
70
|
+
const escaped = escapeHtml(word);
|
|
71
|
+
|
|
72
|
+
if (KEYWORDS.has(word)) {
|
|
73
|
+
result += `<span class="token-keyword">${escaped}</span>`;
|
|
74
|
+
} else {
|
|
75
|
+
result += escaped;
|
|
76
|
+
}
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
result += `<span class="token-keyword">${escaped}</span>`;
|
|
78
|
+
i += word.length;
|
|
79
|
+
continue;
|
|
74
80
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
|
|
82
|
+
// Number
|
|
83
|
+
if (/\d/.test(char)) {
|
|
84
|
+
const num = consumeNumber(line, i);
|
|
85
|
+
result += `<span class="token-number">${escapeHtml(num)}</span>`;
|
|
86
|
+
i += num.length;
|
|
87
|
+
continue;
|
|
78
88
|
}
|
|
79
|
-
|
|
89
|
+
|
|
90
|
+
// Everything else - just escape and append
|
|
91
|
+
result += escapeHtml(char);
|
|
92
|
+
i++;
|
|
93
|
+
}
|
|
80
94
|
|
|
81
95
|
return result || ' ';
|
|
82
96
|
}
|
|
83
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Consume a complete word (identifier)
|
|
100
|
+
*/
|
|
101
|
+
function consumeWord(line: string, start: number): string {
|
|
102
|
+
let i = start;
|
|
103
|
+
while (i < line.length && /[a-zA-Z0-9_$]/.test(line[i])) {
|
|
104
|
+
i++;
|
|
105
|
+
}
|
|
106
|
+
return line.substring(start, i);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Consume a complete number
|
|
111
|
+
*/
|
|
112
|
+
function consumeNumber(line: string, start: number): string {
|
|
113
|
+
let i = start;
|
|
114
|
+
let hasDot = false;
|
|
115
|
+
|
|
116
|
+
while (i < line.length) {
|
|
117
|
+
const char = line[i];
|
|
118
|
+
|
|
119
|
+
if (/\d/.test(char)) {
|
|
120
|
+
i++;
|
|
121
|
+
} else if (char === '.' && !hasDot) {
|
|
122
|
+
hasDot = true;
|
|
123
|
+
i++;
|
|
124
|
+
} else {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return line.substring(start, i);
|
|
130
|
+
}
|
|
131
|
+
|
|
84
132
|
/**
|
|
85
133
|
* Render a parsed line
|
|
86
134
|
*/
|
|
@@ -93,7 +141,7 @@ export function renderLineWithTokens(parsedLine: ParsedLine): string {
|
|
|
93
141
|
*/
|
|
94
142
|
export function getSyntaxHighlightCSS(): string {
|
|
95
143
|
return `
|
|
96
|
-
/* Simple
|
|
144
|
+
/* Simple Code Highlighting */
|
|
97
145
|
.jux-code {
|
|
98
146
|
font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
|
|
99
147
|
font-size: 14px;
|
|
@@ -147,10 +195,13 @@ export function getSyntaxHighlightCSS(): string {
|
|
|
147
195
|
display: none;
|
|
148
196
|
}
|
|
149
197
|
|
|
150
|
-
/* Token colors
|
|
198
|
+
/* Token colors */
|
|
151
199
|
.token-keyword {
|
|
152
200
|
color: #c678dd;
|
|
153
201
|
font-weight: 600;
|
|
202
|
+
}
|
|
203
|
+
.token-number {
|
|
204
|
+
color: #d19a66;
|
|
154
205
|
}
|
|
155
206
|
`;
|
|
156
207
|
}
|