sugar-high 1.0.0 → 1.2.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 +3 -1
- package/lib/index.d.ts +6 -1
- package/lib/index.js +35 -6
- package/lib/presets/index.d.ts +6 -0
- package/lib/presets/index.js +4 -0
- package/lib/presets/lang/c.js +17 -0
- package/lib/presets/lang/clike-base.js +12 -0
- package/lib/presets/lang/diff.js +11 -0
- package/lib/presets/lang/go.js +16 -0
- package/lib/presets/lang/java.js +17 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Super lightweight syntax highlighter for JavaScript and JSX—about **1 kB** minified and gzipped. Works in the browser or any JS runtime that can set HTML strings.
|
|
6
6
|
|
|
7
|
-

|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -33,6 +33,8 @@ import { rust } from 'sugar-high/presets'
|
|
|
33
33
|
const html = highlight(source, { ...rust })
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
Available presets: `c`, `css`, `diff`, `go`, `java`, `python`, and `rust`.
|
|
37
|
+
|
|
36
38
|
For more language presets and syntax color themes, see **[sugar-high.vercel.app](https://sugar-high.vercel.app/)**.
|
|
37
39
|
|
|
38
40
|
## Styling
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
type HighlightOptions = {
|
|
2
2
|
keywords?: Set<string>
|
|
3
|
+
/**
|
|
4
|
+
* Highlighted as the `class` token type (e.g. built-in types). Checked before `keywords`.
|
|
5
|
+
*/
|
|
6
|
+
typeKeywords?: Set<string>
|
|
3
7
|
onCommentStart?: (curr: string, next: string) => number | boolean
|
|
4
8
|
onCommentEnd?: (curr: string, prev: string) => number | boolean
|
|
5
9
|
/**
|
|
@@ -7,11 +11,12 @@ type HighlightOptions = {
|
|
|
7
11
|
* or null/undefined or a number below 1 for default JS single-quoted string rules.
|
|
8
12
|
*/
|
|
9
13
|
onQuote?: (curr: string, i: number, code: string) => number | null | undefined
|
|
14
|
+
lineClassName?: (line: string, index: number) => string | null | undefined
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
export function highlight(code: string, options?: HighlightOptions): string
|
|
13
18
|
export function tokenize(code: string, options?: HighlightOptions): Array<[number, string]>
|
|
14
|
-
export function generate(tokens: Array<[number, string]>): Array<any>
|
|
19
|
+
export function generate(tokens: Array<[number, string]>, options?: Pick<HighlightOptions, 'lineClassName'>): Array<any>
|
|
15
20
|
export const SugarHigh: {
|
|
16
21
|
TokenTypes: {
|
|
17
22
|
[key: number]: string
|
package/lib/index.js
CHANGED
|
@@ -69,6 +69,13 @@ const Keywords_Ts = new Set([
|
|
|
69
69
|
'unknown',
|
|
70
70
|
'never',
|
|
71
71
|
'any',
|
|
72
|
+
// Built-in type names (annotations / type positions)
|
|
73
|
+
'number',
|
|
74
|
+
'string',
|
|
75
|
+
'boolean',
|
|
76
|
+
'bigint',
|
|
77
|
+
'symbol',
|
|
78
|
+
'object',
|
|
72
79
|
])
|
|
73
80
|
|
|
74
81
|
const Signs = new Set([
|
|
@@ -298,9 +305,11 @@ function isRegexStart(str) {
|
|
|
298
305
|
* @param {string} code
|
|
299
306
|
* @param {{
|
|
300
307
|
* keywords?: Set<string>
|
|
308
|
+
* typeKeywords?: Set<string>
|
|
301
309
|
* onCommentStart?: (curr: string, next: string) => number | boolean
|
|
302
310
|
* onCommentEnd?: (prev: string, curr: string) => number | boolean
|
|
303
311
|
* onQuote?: (curr: string, i: number, code: string) => number | null | undefined
|
|
312
|
+
* lineClassName?: (line: string, index: number) => string | null | undefined
|
|
304
313
|
* } | undefined} options
|
|
305
314
|
* Optional `onQuote(curr, i, code)` at `code[i] === "'"`: return length to consume from `i` (>= 1),
|
|
306
315
|
* or null/undefined/below 1 for default JS single-quoted strings. No substring allocation.
|
|
@@ -319,6 +328,9 @@ function tokenize(code, options) {
|
|
|
319
328
|
onCommentEnd,
|
|
320
329
|
} = mergedOptions
|
|
321
330
|
|
|
331
|
+
const resolvedTypeKeywords =
|
|
332
|
+
mergedOptions.typeKeywords instanceof Set ? mergedOptions.typeKeywords : null
|
|
333
|
+
|
|
322
334
|
let current = ''
|
|
323
335
|
let type = -1
|
|
324
336
|
/** @type {[number, string]} */
|
|
@@ -388,6 +400,8 @@ function tokenize(code, options) {
|
|
|
388
400
|
// Determine strings first before other types
|
|
389
401
|
if (inStringQuotes() || inStrTemplateLiterals()) {
|
|
390
402
|
return T_STRING
|
|
403
|
+
} else if (resolvedTypeKeywords && resolvedTypeKeywords.has(token)) {
|
|
404
|
+
return last[1] === '.' ? T_IDENTIFIER : T_CLS_NUMBER
|
|
391
405
|
} else if (resolvedKeywords.has(token)) {
|
|
392
406
|
return last[1] === '.' ? T_IDENTIFIER : T_KEYWORD
|
|
393
407
|
} else if (isLineBreak) {
|
|
@@ -800,29 +814,42 @@ function tokenize(code, options) {
|
|
|
800
814
|
|
|
801
815
|
/**
|
|
802
816
|
* @param {Array<[number, string]>} tokens
|
|
817
|
+
* @param {{
|
|
818
|
+
* lineClassName?: (line: string, index: number) => string | null | undefined
|
|
819
|
+
* } | undefined} options
|
|
803
820
|
* @return {Array<{type: string, tagName: string, children: any[], properties: Record<string, string>}>}
|
|
804
821
|
*/
|
|
805
|
-
function generate(tokens) {
|
|
822
|
+
function generate(tokens, options) {
|
|
806
823
|
const lines = []
|
|
824
|
+
const lineClassName = options && typeof options.lineClassName === 'function'
|
|
825
|
+
? options.lineClassName
|
|
826
|
+
: null
|
|
827
|
+
let lineIndex = 0
|
|
807
828
|
/**
|
|
808
829
|
* @param {any} children
|
|
830
|
+
* @param {string} text
|
|
809
831
|
* @return {{type: string, tagName: string, children: any[], properties: Record<string, string>}}
|
|
810
832
|
*/
|
|
811
|
-
const createLine = (children) =>
|
|
812
|
-
|
|
833
|
+
const createLine = (children, text) => {
|
|
834
|
+
const extraClassName = lineClassName ? lineClassName(text, lineIndex) : ''
|
|
835
|
+
lineIndex++
|
|
836
|
+
|
|
837
|
+
return ({
|
|
813
838
|
type: 'element',
|
|
814
839
|
tagName: 'span',
|
|
815
840
|
children,
|
|
816
841
|
properties: {
|
|
817
|
-
className: 'sh__line',
|
|
842
|
+
className: extraClassName ? `sh__line ${extraClassName}` : 'sh__line',
|
|
818
843
|
},
|
|
819
844
|
})
|
|
845
|
+
}
|
|
820
846
|
|
|
821
847
|
/**
|
|
822
848
|
* @param {Array<[number, string]>} tokens
|
|
823
849
|
* @returns {void}
|
|
824
850
|
*/
|
|
825
851
|
function flushLine(tokens) {
|
|
852
|
+
const lineText = tokens.map(([, value]) => value).join('')
|
|
826
853
|
/** @type {Array<any>} */
|
|
827
854
|
const lineTokens = (
|
|
828
855
|
tokens
|
|
@@ -842,7 +869,7 @@ function generate(tokens) {
|
|
|
842
869
|
}
|
|
843
870
|
})
|
|
844
871
|
)
|
|
845
|
-
lines.push(createLine(lineTokens))
|
|
872
|
+
lines.push(createLine(lineTokens, lineText))
|
|
846
873
|
}
|
|
847
874
|
/** @type {Array<[number, string]>} */
|
|
848
875
|
const lineTokens = []
|
|
@@ -928,16 +955,18 @@ function toHtml(lines) {
|
|
|
928
955
|
* @param {string} code
|
|
929
956
|
* @param {{
|
|
930
957
|
* keywords?: Set<string>
|
|
958
|
+
* typeKeywords?: Set<string>
|
|
931
959
|
* onCommentStart?: (curr: string, next: string) => number | boolean
|
|
932
960
|
* onCommentEnd?: (curr: string, prev: string) => number | boolean
|
|
933
961
|
* onQuote?: (curr: string, i: number, code: string) => number | null | undefined
|
|
962
|
+
* lineClassName?: (line: string, index: number) => string | null | undefined
|
|
934
963
|
* } | undefined} options
|
|
935
964
|
* `onQuote` same as `tokenize`.
|
|
936
965
|
* @returns {string}
|
|
937
966
|
*/
|
|
938
967
|
function highlight(code, options) {
|
|
939
968
|
const tokens = tokenize(code, options)
|
|
940
|
-
const lines = generate(tokens)
|
|
969
|
+
const lines = generate(tokens, options)
|
|
941
970
|
const output = toHtml(lines)
|
|
942
971
|
return output
|
|
943
972
|
}
|
package/lib/presets/index.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
type LanguageConfig = {
|
|
2
2
|
keywords: Set<string>
|
|
3
|
+
typeKeywords?: Set<string>
|
|
3
4
|
onCommentStart?(curr: string, next: string): 0 | 1 | 2
|
|
4
5
|
onCommentEnd?(prev: string, curr: string): 0 | 1 | 2
|
|
5
6
|
onQuote?(curr: string, i: number, code: string): number | null | undefined
|
|
7
|
+
lineClassName?(line: string, index: number): string | null | undefined
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
export const css: LanguageConfig
|
|
9
11
|
export const rust: LanguageConfig
|
|
10
12
|
export const python: LanguageConfig
|
|
13
|
+
export const c: LanguageConfig
|
|
14
|
+
export const go: LanguageConfig
|
|
15
|
+
export const java: LanguageConfig
|
|
16
|
+
export const diff: LanguageConfig
|
package/lib/presets/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export * as css from './lang/css.js'
|
|
2
2
|
export * as rust from './lang/rust.js'
|
|
3
3
|
export * as python from './lang/python.js'
|
|
4
|
+
export * as c from './lang/c.js'
|
|
5
|
+
export * as go from './lang/go.js'
|
|
6
|
+
export * as java from './lang/java.js'
|
|
7
|
+
export * as diff from './lang/diff.js'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { onCommentStart, onCommentEnd } from './clike-base.js'
|
|
3
|
+
|
|
4
|
+
export const typeKeywords = new Set([
|
|
5
|
+
'void', 'char', 'short', 'int', 'long', 'float', 'double', 'signed', 'unsigned',
|
|
6
|
+
'_Bool', '_Complex', '_Imaginary',
|
|
7
|
+
])
|
|
8
|
+
|
|
9
|
+
export const keywords = new Set([
|
|
10
|
+
'auto', 'break', 'case', 'const', 'continue', 'default', 'do', 'else', 'enum',
|
|
11
|
+
'extern', 'for', 'goto', 'if', 'inline', 'register', 'restrict', 'return',
|
|
12
|
+
'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'volatile', 'while',
|
|
13
|
+
'_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn', '_Static_assert',
|
|
14
|
+
'_Thread_local',
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
export { onCommentStart, onCommentEnd }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
export const onCommentStart = (currentChar, nextChar) => {
|
|
3
|
+
const pair = currentChar + nextChar
|
|
4
|
+
if (pair === '//') return 1
|
|
5
|
+
if (pair === '/*') return 1
|
|
6
|
+
return 0
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const onCommentEnd = (prevChar, currChar) => {
|
|
10
|
+
if (currChar === '\n') return 1
|
|
11
|
+
return prevChar + currChar === '*/' ? 1 : 0
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const keywords = new Set([])
|
|
4
|
+
|
|
5
|
+
export const lineClassName = (line) => {
|
|
6
|
+
if (line.startsWith('+') && !line.startsWith('+++')) return 'sh__line--diff-add'
|
|
7
|
+
if (line.startsWith('-') && !line.startsWith('---')) return 'sh__line--diff-remove'
|
|
8
|
+
if (line.startsWith('@@')) return 'sh__line--diff-hunk'
|
|
9
|
+
if (/^(diff --git|index |--- |\+\+\+ )/.test(line)) return 'sh__line--diff-meta'
|
|
10
|
+
return undefined
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { onCommentStart, onCommentEnd } from './clike-base.js'
|
|
3
|
+
|
|
4
|
+
export const typeKeywords = new Set([
|
|
5
|
+
'bool', 'byte', 'complex64', 'complex128', 'error', 'float32', 'float64', 'int',
|
|
6
|
+
'int8', 'int16', 'int32', 'int64', 'rune', 'string', 'uint', 'uint8', 'uint16',
|
|
7
|
+
'uint32', 'uint64', 'uintptr',
|
|
8
|
+
])
|
|
9
|
+
|
|
10
|
+
export const keywords = new Set([
|
|
11
|
+
'break', 'case', 'chan', 'const', 'continue', 'default', 'defer', 'else',
|
|
12
|
+
'fallthrough', 'for', 'func', 'go', 'goto', 'if', 'import', 'interface', 'map',
|
|
13
|
+
'package', 'range', 'return', 'select', 'struct', 'switch', 'type', 'var',
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
export { onCommentStart, onCommentEnd }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { onCommentStart, onCommentEnd } from './clike-base.js'
|
|
3
|
+
|
|
4
|
+
export const typeKeywords = new Set([
|
|
5
|
+
'boolean', 'byte', 'char', 'double', 'float', 'int', 'long', 'short', 'void',
|
|
6
|
+
])
|
|
7
|
+
|
|
8
|
+
export const keywords = new Set([
|
|
9
|
+
'abstract', 'assert', 'break', 'case', 'catch', 'class', 'const', 'continue',
|
|
10
|
+
'default', 'do', 'else', 'enum', 'extends', 'final', 'finally', 'for', 'goto',
|
|
11
|
+
'if', 'implements', 'import', 'instanceof', 'interface', 'native', 'new',
|
|
12
|
+
'package', 'private', 'protected', 'public', 'return', 'static', 'strictfp',
|
|
13
|
+
'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient',
|
|
14
|
+
'try', 'volatile', 'while',
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
export { onCommentStart, onCommentEnd }
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sugar-high",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/huozhi/sugar-high.git",
|
|
7
|
+
"directory": "packages/sugar-high"
|
|
8
|
+
},
|
|
4
9
|
"type": "module",
|
|
5
10
|
"types": "./lib/index.d.ts",
|
|
6
11
|
"main": "./lib/index.js",
|
|
@@ -25,7 +30,7 @@
|
|
|
25
30
|
},
|
|
26
31
|
"devDependencies": {
|
|
27
32
|
"@types/node": "22.12.0",
|
|
28
|
-
"typescript": "
|
|
33
|
+
"typescript": "6.0.2",
|
|
29
34
|
"vitest": "^3.0.2"
|
|
30
35
|
}
|
|
31
36
|
}
|