sugar-high 1.1.0 → 1.2.1

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 CHANGED
@@ -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
@@ -78,11 +80,24 @@ pre code {
78
80
  Target a line with `.sh__line:nth-child(<n>)` (1-based):
79
81
 
80
82
  ```css
83
+ .sh__line {
84
+ display: block;
85
+ padding: 0 12px;
86
+ margin: 0 -12px;
87
+ min-height: 1rem;
88
+ }
89
+
81
90
  .sh__line:nth-child(5) {
82
- background: #f5f5f5;
91
+ background: #fff8c5;
92
+ }
93
+
94
+ .sh__line--highlighted {
95
+ background: #fff8c5;
83
96
  }
84
97
  ```
85
98
 
99
+ Use `.sh__line--highlighted` when you add highlight classes yourself (for example with `lineClassName`).
100
+
86
101
  ## Remark
87
102
 
88
103
  Use the [remark plugin](https://sugar-high.vercel.app/remark) to highlight fenced code blocks when processing Markdown with [remark](https://remark.js.org/). Details: [`packages/remark-sugar-high`](https://github.com/huozhi/sugar-high/tree/main/packages/remark-sugar-high).
package/lib/index.d.ts CHANGED
@@ -11,11 +11,12 @@ type HighlightOptions = {
11
11
  * or null/undefined or a number below 1 for default JS single-quoted string rules.
12
12
  */
13
13
  onQuote?: (curr: string, i: number, code: string) => number | null | undefined
14
+ lineClassName?: (line: string, index: number) => string | null | undefined
14
15
  }
15
16
 
16
17
  export function highlight(code: string, options?: HighlightOptions): string
17
18
  export function tokenize(code: string, options?: HighlightOptions): Array<[number, string]>
18
- export function generate(tokens: Array<[number, string]>): Array<any>
19
+ export function generate(tokens: Array<[number, string]>, options?: Pick<HighlightOptions, 'lineClassName'>): Array<any>
19
20
  export const SugarHigh: {
20
21
  TokenTypes: {
21
22
  [key: number]: string
package/lib/index.js CHANGED
@@ -309,6 +309,7 @@ function isRegexStart(str) {
309
309
  * onCommentStart?: (curr: string, next: string) => number | boolean
310
310
  * onCommentEnd?: (prev: string, curr: string) => number | boolean
311
311
  * onQuote?: (curr: string, i: number, code: string) => number | null | undefined
312
+ * lineClassName?: (line: string, index: number) => string | null | undefined
312
313
  * } | undefined} options
313
314
  * Optional `onQuote(curr, i, code)` at `code[i] === "'"`: return length to consume from `i` (>= 1),
314
315
  * or null/undefined/below 1 for default JS single-quoted strings. No substring allocation.
@@ -714,9 +715,16 @@ function tokenize(code, options) {
714
715
 
715
716
  let foundClose = false
716
717
 
718
+ // `/` is literal inside regex character classes, e.g. `[/]`.
719
+ let inCharClass = false
720
+
717
721
  // traverse to find closing regex slash
718
722
  for (; !isEol(); i++) {
719
- if (code[i] === '/' && code[i - 1] !== '\\') {
723
+ const ch = code[i]
724
+ const escaped = code[i - 1] === '\\'
725
+ if (!escaped && ch === '[') inCharClass = true
726
+ if (!escaped && ch === ']') inCharClass = false
727
+ if (ch === '/' && !inCharClass && !escaped) {
720
728
  foundClose = true
721
729
  // end of regex, append regex flags
722
730
  while (start !== i && /^[a-z]$/.test(code[i + 1]) && !isEol()) {
@@ -813,29 +821,42 @@ function tokenize(code, options) {
813
821
 
814
822
  /**
815
823
  * @param {Array<[number, string]>} tokens
824
+ * @param {{
825
+ * lineClassName?: (line: string, index: number) => string | null | undefined
826
+ * } | undefined} options
816
827
  * @return {Array<{type: string, tagName: string, children: any[], properties: Record<string, string>}>}
817
828
  */
818
- function generate(tokens) {
829
+ function generate(tokens, options) {
819
830
  const lines = []
831
+ const lineClassName = options && typeof options.lineClassName === 'function'
832
+ ? options.lineClassName
833
+ : null
834
+ let lineIndex = 0
820
835
  /**
821
836
  * @param {any} children
837
+ * @param {string} text
822
838
  * @return {{type: string, tagName: string, children: any[], properties: Record<string, string>}}
823
839
  */
824
- const createLine = (children) =>
825
- ({
840
+ const createLine = (children, text) => {
841
+ const extraClassName = lineClassName ? lineClassName(text, lineIndex) : ''
842
+ lineIndex++
843
+
844
+ return ({
826
845
  type: 'element',
827
846
  tagName: 'span',
828
847
  children,
829
848
  properties: {
830
- className: 'sh__line',
849
+ className: extraClassName ? `sh__line ${extraClassName}` : 'sh__line',
831
850
  },
832
851
  })
852
+ }
833
853
 
834
854
  /**
835
855
  * @param {Array<[number, string]>} tokens
836
856
  * @returns {void}
837
857
  */
838
858
  function flushLine(tokens) {
859
+ const lineText = tokens.map(([, value]) => value).join('')
839
860
  /** @type {Array<any>} */
840
861
  const lineTokens = (
841
862
  tokens
@@ -855,7 +876,7 @@ function generate(tokens) {
855
876
  }
856
877
  })
857
878
  )
858
- lines.push(createLine(lineTokens))
879
+ lines.push(createLine(lineTokens, lineText))
859
880
  }
860
881
  /** @type {Array<[number, string]>} */
861
882
  const lineTokens = []
@@ -945,13 +966,14 @@ function toHtml(lines) {
945
966
  * onCommentStart?: (curr: string, next: string) => number | boolean
946
967
  * onCommentEnd?: (curr: string, prev: string) => number | boolean
947
968
  * onQuote?: (curr: string, i: number, code: string) => number | null | undefined
969
+ * lineClassName?: (line: string, index: number) => string | null | undefined
948
970
  * } | undefined} options
949
971
  * `onQuote` same as `tokenize`.
950
972
  * @returns {string}
951
973
  */
952
974
  function highlight(code, options) {
953
975
  const tokens = tokenize(code, options)
954
- const lines = generate(tokens)
976
+ const lines = generate(tokens, options)
955
977
  const output = toHtml(lines)
956
978
  return output
957
979
  }
@@ -4,6 +4,7 @@ type LanguageConfig = {
4
4
  onCommentStart?(curr: string, next: string): 0 | 1 | 2
5
5
  onCommentEnd?(prev: string, curr: string): 0 | 1 | 2
6
6
  onQuote?(curr: string, i: number, code: string): number | null | undefined
7
+ lineClassName?(line: string, index: number): string | null | undefined
7
8
  }
8
9
 
9
10
  export const css: LanguageConfig
@@ -12,3 +13,4 @@ export const python: LanguageConfig
12
13
  export const c: LanguageConfig
13
14
  export const go: LanguageConfig
14
15
  export const java: LanguageConfig
16
+ export const diff: LanguageConfig
@@ -4,3 +4,4 @@ export * as python from './lang/python.js'
4
4
  export * as c from './lang/c.js'
5
5
  export * as go from './lang/go.js'
6
6
  export * as java from './lang/java.js'
7
+ export * as diff from './lang/diff.js'
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sugar-high",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/huozhi/sugar-high.git",