sugar-high 0.2.2 → 0.4.0
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/README.md +27 -19
- package/lib/index.mjs +71 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
Super lightweight JSX syntax highlighter, around 1KB after minified and gzipped
|
|
5
5
|
|
|
6
|
-

|
|
7
7
|
|
|
8
8
|
### Usage
|
|
9
9
|
|
|
@@ -35,26 +35,34 @@ Then make your own theme with customized colors by token type and put in global
|
|
|
35
35
|
* comment
|
|
36
36
|
* jsxliterals
|
|
37
37
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
:root {
|
|
39
|
+
--sh-class: #2d5e9d;
|
|
40
|
+
--sh-identifier: #354150;
|
|
41
|
+
--sh-sign: #8996a3;
|
|
42
|
+
--sh-string: #00a99a;
|
|
43
|
+
--sh-keyword: #f47067;
|
|
44
|
+
--sh-comment: #a19595;
|
|
45
|
+
--sh-jsxliterals: #6266d1;
|
|
40
46
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
.sh__comment {
|
|
54
|
-
color: #a19595;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Features
|
|
50
|
+
|
|
51
|
+
#### Line number
|
|
52
|
+
|
|
53
|
+
Sugar high provide `.sh_line` class name for each line. To display line number, define the `.sh_line::before` element with CSS will enable line numbers automatically.
|
|
54
|
+
|
|
55
|
+
```css
|
|
56
|
+
pre code {
|
|
57
|
+
counter-reset: sh-line-number;
|
|
55
58
|
}
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
|
|
60
|
+
.sh__line::before {
|
|
61
|
+
counter-increment: sh-line-number 1;
|
|
62
|
+
content: counter(sh-line-number);
|
|
63
|
+
margin-right: 24px;
|
|
64
|
+
text-align: right;
|
|
65
|
+
color: #a4a4a4;
|
|
58
66
|
}
|
|
59
67
|
```
|
|
60
68
|
|
package/lib/index.mjs
CHANGED
|
@@ -123,14 +123,19 @@ function encode(str) {
|
|
|
123
123
|
.replace(/>/g, '>')
|
|
124
124
|
.replace(/"/g, '"')
|
|
125
125
|
.replace(/'/g, ''')
|
|
126
|
-
// matches space but not new line
|
|
127
|
-
.replace(/[^\S\r\n]/g, ' ')
|
|
128
126
|
}
|
|
129
127
|
|
|
130
128
|
function isWord(chr) {
|
|
131
129
|
return /^[\w_]+$/.test(chr)
|
|
132
130
|
}
|
|
133
131
|
|
|
132
|
+
function isCls(str) {
|
|
133
|
+
const chr0 = str[0]
|
|
134
|
+
return isWord(chr0) &&
|
|
135
|
+
chr0 === chr0.toUpperCase() ||
|
|
136
|
+
str === 'null'
|
|
137
|
+
}
|
|
138
|
+
|
|
134
139
|
function isIdentifierChar(chr) {
|
|
135
140
|
return /^[a-zA-Z_$]$/.test(chr)
|
|
136
141
|
}
|
|
@@ -163,16 +168,22 @@ export function tokenize(code) {
|
|
|
163
168
|
/** @type {Array<[number, string]>} */
|
|
164
169
|
const tokens = []
|
|
165
170
|
|
|
166
|
-
// jsx.tag for entering open or closed
|
|
171
|
+
// jsx.tag for entering open or closed tags
|
|
172
|
+
// jsx.close for is the closing sign of open or close tags
|
|
167
173
|
// jsx.child for entering children
|
|
168
174
|
// jsx.expr for entering {expression}
|
|
169
|
-
/** @type {{ tag: boolean; child: boolean; expr: boolean }} */
|
|
170
|
-
const jsx = { tag: false, child: false, expr: false }
|
|
175
|
+
/** @type {{ tag: boolean; close: boolean; child: boolean; expr: boolean }} */
|
|
176
|
+
const jsx = { tag: false, close: false, child: false, expr: false }
|
|
171
177
|
|
|
172
|
-
const
|
|
178
|
+
const inJsxTag = () => jsx.tag || jsx.close
|
|
179
|
+
const inJsxLiterals = () => !inJsxTag() && jsx.child && !jsx.expr
|
|
173
180
|
|
|
181
|
+
/**
|
|
182
|
+
*
|
|
183
|
+
* @param {string} token
|
|
184
|
+
* @returns {number}
|
|
185
|
+
*/
|
|
174
186
|
function classify(token) {
|
|
175
|
-
const chr0 = token[0]
|
|
176
187
|
if (keywords.has(token)) {
|
|
177
188
|
return T_KEYWORD
|
|
178
189
|
} else if (token === '\n') {
|
|
@@ -181,17 +192,12 @@ export function tokenize(code) {
|
|
|
181
192
|
return T_SPACE
|
|
182
193
|
} else if (token.split('').every(ch => signs.has(ch))) {
|
|
183
194
|
return T_SIGN
|
|
184
|
-
} else if (
|
|
185
|
-
|
|
186
|
-
(
|
|
187
|
-
isWord(chr0) &&
|
|
188
|
-
chr0 === chr0.toUpperCase() ||
|
|
189
|
-
token === 'null'
|
|
190
|
-
)
|
|
191
|
-
) {
|
|
192
|
-
return T_CLS_NUMBER
|
|
195
|
+
} else if (isCls(token)) {
|
|
196
|
+
return inJsxLiterals() ? T_JSX_LITERALS : inJsxTag() ? T_IDENTIFIER : T_CLS_NUMBER
|
|
193
197
|
} else {
|
|
194
|
-
return
|
|
198
|
+
return inJsxLiterals()
|
|
199
|
+
? T_JSX_LITERALS
|
|
200
|
+
: isIdentifier(token) ? T_IDENTIFIER : T_STRING
|
|
195
201
|
}
|
|
196
202
|
}
|
|
197
203
|
|
|
@@ -211,15 +217,19 @@ export function tokenize(code) {
|
|
|
211
217
|
const curr = code[i]
|
|
212
218
|
const prev = code[i - 1]
|
|
213
219
|
const next = code[i + 1]
|
|
214
|
-
const c_n = curr + next // current and next
|
|
215
220
|
const p_c = prev + curr // previous and current
|
|
221
|
+
const c_n = curr + next // current and next
|
|
216
222
|
const isJsxLiterals = inJsxLiterals()
|
|
217
223
|
|
|
224
|
+
if (jsx.close && (last[1] === '>' || last[1] === '/>')) {
|
|
225
|
+
jsx.close = false
|
|
226
|
+
}
|
|
218
227
|
if (jsx.tag) {
|
|
219
228
|
const isOpenElementEnd = curr === '>'
|
|
220
229
|
const isCloseElementEnd = p_c === '/>'
|
|
221
|
-
jsx.child = !isCloseElementEnd
|
|
230
|
+
jsx.child = !isCloseElementEnd
|
|
222
231
|
jsx.tag = !(isOpenElementEnd || isCloseElementEnd)
|
|
232
|
+
jsx.close = isOpenElementEnd || isCloseElementEnd
|
|
223
233
|
}
|
|
224
234
|
// if it's not in a jsx tag declaration or a string, close child if next is jsx close tag
|
|
225
235
|
if (!jsx.tag && (curr === '<' && isIdentifierChar(next) || c_n === '</')) {
|
|
@@ -319,6 +329,9 @@ export function tokenize(code) {
|
|
|
319
329
|
!signs.has(curr)
|
|
320
330
|
) {
|
|
321
331
|
current += curr
|
|
332
|
+
if (next === '<') {
|
|
333
|
+
append();
|
|
334
|
+
}
|
|
322
335
|
} else {
|
|
323
336
|
append()
|
|
324
337
|
current = curr
|
|
@@ -349,16 +362,46 @@ export function tokenize(code) {
|
|
|
349
362
|
* @return {Array<string>}
|
|
350
363
|
*/
|
|
351
364
|
function generate(tokens) {
|
|
352
|
-
const
|
|
365
|
+
const linesHtml = []
|
|
366
|
+
const createLine = (content) => `<span class="sh__line">${content}</span>`
|
|
367
|
+
|
|
368
|
+
function flushLine(tokens) {
|
|
369
|
+
linesHtml.push(createLine(
|
|
370
|
+
tokens.map(([type, value]) => (
|
|
371
|
+
`<span style="color: var(--sh-${types[type]})">${encode(value)}</span>`
|
|
372
|
+
))
|
|
373
|
+
.join('')
|
|
374
|
+
))
|
|
375
|
+
}
|
|
376
|
+
const lineTokens = []
|
|
353
377
|
for (let i = 0; i < tokens.length; i++) {
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
378
|
+
const token = tokens[i]
|
|
379
|
+
const [type, value] = token
|
|
380
|
+
if (type !== T_BREAK) {
|
|
381
|
+
// Divide multi-line token into multi-line code
|
|
382
|
+
if (value.includes('\n')) {
|
|
383
|
+
const lines = value.split('\n')
|
|
384
|
+
for (let j = 0; j < lines.length; j++) {
|
|
385
|
+
lineTokens.push([type, lines[j]])
|
|
386
|
+
if (j < lines.length - 1) {
|
|
387
|
+
flushLine(lineTokens)
|
|
388
|
+
lineTokens.length = 0
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
lineTokens.push(token)
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
lineTokens.push([type, ''])
|
|
396
|
+
flushLine(lineTokens)
|
|
397
|
+
lineTokens.length = 0
|
|
398
|
+
}
|
|
360
399
|
}
|
|
361
|
-
|
|
400
|
+
|
|
401
|
+
if (lineTokens.length)
|
|
402
|
+
flushLine(lineTokens)
|
|
403
|
+
|
|
404
|
+
return linesHtml
|
|
362
405
|
}
|
|
363
406
|
|
|
364
407
|
/**
|
|
@@ -368,6 +411,6 @@ function generate(tokens) {
|
|
|
368
411
|
*/
|
|
369
412
|
export function highlight(code) {
|
|
370
413
|
const tokens = tokenize(code)
|
|
371
|
-
const output = generate(tokens).join('')
|
|
414
|
+
const output = generate(tokens).join('\n')
|
|
372
415
|
return output
|
|
373
416
|
}
|