sugar-high 2.1.0 → 2.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
@@ -56,8 +56,8 @@ behavior.
56
56
  ## Built-in languages
57
57
 
58
58
  `javascript`, `typescript`, `css`, `python`, `c`, `go`, `java`, `rust`, `json`, `diff`, `shell`,
59
- `cpp`, `csharp`, `sql`, `html`, `yaml`, `markdown`, `kotlin`, `swift`, `php`, `toml`, `powershell`,
60
- `dockerfile`, `graphql`, and `hcl`.
59
+ `cpp`, `csharp`, `sql`, `html`, `yaml`, `markdown`, `plaintext`, `ruby`, `kotlin`, `swift`, `php`,
60
+ `toml`, `powershell`, `dockerfile`, `graphql`, and `hcl`.
61
61
 
62
62
  Related dialects share one implementation: JavaScript includes JSX, TypeScript includes TSX, JSON
63
63
  includes JSONC comments, Shell includes sh/Bash/Zsh, and HCL includes Terraform.
package/lib/core.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  import {
4
- assemble, generate, SugarHigh, toHtml, T_BREAK, T_CLASS, T_COMMENT, T_IDENTIFIER,
4
+ assemble, generate, render, SugarHigh, T_BREAK, T_CLASS, T_COMMENT, T_IDENTIFIER,
5
5
  T_KEYWORD, T_PROPERTY, T_SIGN, T_SPACE, T_STRING,
6
6
  } from './shared.js'
7
7
 
@@ -147,11 +147,6 @@ function parse(code, options) {
147
147
  return parsed
148
148
  }
149
149
 
150
- /** @param {ParsedCode} parsed @param {DisplayOptions | undefined} options */
151
- function render(parsed, options) {
152
- return toHtml(generate(parsed, options))
153
- }
154
-
155
150
  export { generate, parse, render, SugarHigh, tokenize }
156
151
 
157
152
  /**
package/lib/index.d.ts CHANGED
@@ -25,6 +25,8 @@ export type LanguageName =
25
25
  | 'html'
26
26
  | 'yaml'
27
27
  | 'markdown'
28
+ | 'plaintext'
29
+ | 'ruby'
28
30
  | 'kotlin'
29
31
  | 'swift'
30
32
  | 'php'
@@ -0,0 +1 @@
1
+ export function tokenize(code: string): Array<[number, string]>;
@@ -0,0 +1,5 @@
1
+ // @ts-check
2
+ import { T_IDENTIFIER } from '../shared.js'
3
+
4
+ /** @param {string} code */
5
+ export const tokenize = (code) => [[T_IDENTIFIER, code]]
@@ -0,0 +1,13 @@
1
+ export const keywords: Set<string>;
2
+ export function onCommentStart(
3
+ curr: string,
4
+ next: string,
5
+ index: number,
6
+ code: string,
7
+ ): 0 | 1 | 2;
8
+ export function onCommentEnd(
9
+ prev: string,
10
+ curr: string,
11
+ index: number,
12
+ code: string,
13
+ ): 0 | 1 | 2;
@@ -0,0 +1,41 @@
1
+ // @ts-check
2
+
3
+ export const keywords = new Set([
4
+ 'BEGIN', 'END', '__ENCODING__', '__FILE__', '__LINE__', 'alias', 'and', 'begin', 'break',
5
+ 'case', 'class', 'def', 'defined', 'do', 'else', 'elsif', 'end', 'ensure', 'false', 'for', 'if',
6
+ 'in', 'module', 'next', 'nil', 'not', 'or', 'redo', 'rescue', 'retry', 'return', 'self', 'super',
7
+ 'then', 'true', 'undef', 'unless', 'until', 'when', 'while', 'yield',
8
+ ])
9
+
10
+ /** @param {number} index @param {string} code */
11
+ function isBlockCommentStart(index, code) {
12
+ if (index > 0 && code[index - 1] !== '\n') return false
13
+ return /^=begin(?:\s|$)/.test(code.slice(index))
14
+ }
15
+
16
+ /**
17
+ * @param {string} curr
18
+ * @param {string} _next
19
+ * @param {number} index
20
+ * @param {string} code
21
+ */
22
+ export function onCommentStart(curr, _next, index, code) {
23
+ if (curr === '#') return 1
24
+ if (curr === '=' && isBlockCommentStart(index, code)) return 2
25
+ return 0
26
+ }
27
+
28
+ /**
29
+ * @param {string} _prev
30
+ * @param {string} curr
31
+ * @param {number} index
32
+ * @param {string} code
33
+ */
34
+ export function onCommentEnd(_prev, curr, index, code) {
35
+ if (curr !== '\n') return 0
36
+
37
+ const lineStart = code.lastIndexOf('\n', index - 1) + 1
38
+ const line = code.slice(lineStart, index)
39
+ if (/^=end(?:\s|$)/.test(line)) return 2
40
+ return 1
41
+ }
package/lib/lang.js CHANGED
@@ -16,8 +16,10 @@ import * as javascript from './lang/javascript.js'
16
16
  import * as kotlin from './lang/kotlin.js'
17
17
  import * as markdown from './lang/markdown.js'
18
18
  import * as php from './lang/php.js'
19
+ import * as plaintext from './lang/plaintext.js'
19
20
  import * as powershell from './lang/powershell.js'
20
21
  import * as python from './lang/python.js'
22
+ import * as ruby from './lang/ruby.js'
21
23
  import * as rust from './lang/rust.js'
22
24
  import * as shell from './lang/shell.js'
23
25
  import * as sql from './lang/sql.js'
@@ -69,6 +71,8 @@ const languages = [
69
71
  { id: 'html', extension: 'html', aliases: ['htm', 'xml'], config: html },
70
72
  { id: 'yaml', extension: 'yaml', aliases: ['yml'], config: nonJavaScript(yaml) },
71
73
  { id: 'markdown', extension: 'md', aliases: ['md', 'mdx'], config: nonJavaScript(markdown) },
74
+ { id: 'plaintext', extension: 'txt', aliases: ['text', 'plain'], config: nonJavaScript(plaintext) },
75
+ { id: 'ruby', extension: 'rb', aliases: [], config: nonJavaScript(ruby) },
72
76
  { id: 'kotlin', extension: 'kt', aliases: ['kts'], config: nonJavaScript(kotlin) },
73
77
  { id: 'swift', extension: 'swift', aliases: [], config: nonJavaScript(swift) },
74
78
  { id: 'php', extension: 'php', aliases: [], config: nonJavaScript(php) },
package/lib/shared.js CHANGED
@@ -64,6 +64,42 @@ function assemble(value, tokens) {
64
64
  return { value, lines }
65
65
  }
66
66
 
67
+ /**
68
+ * @param {import('./core.js').ParsedLine} parsedLine
69
+ * @param {import('./core.js').DisplayOptions['markLine']} markLine
70
+ */
71
+ function createLine(parsedLine, markLine) {
72
+ const line = {
73
+ index: parsedLine.index,
74
+ value: parsedLine.value,
75
+ tokens: parsedLine.tokens,
76
+ annotations: parsedLine.annotations,
77
+ className: `sh__line${parsedLine.annotations.map(annotation => ` sh__line--${annotation}`).join('')}`,
78
+ style: {},
79
+ properties: {},
80
+ }
81
+ markLine?.(line)
82
+ return line
83
+ }
84
+
85
+ /**
86
+ * @param {import('./core.js').ParsedToken} parsedToken
87
+ * @param {import('./core.js').DisplayOptions['cx']} cx
88
+ * @param {import('./core.js').DisplayOptions['mark']} mark
89
+ */
90
+ function createToken({ type, value }, cx, mark) {
91
+ const extraClassName = cx?.[type]
92
+ const token = {
93
+ type,
94
+ value,
95
+ className: `sh__token--${type}${extraClassName ? ` ${extraClassName}` : ''}`,
96
+ style: { color: `var(--sh-${type})` },
97
+ properties: {},
98
+ }
99
+ mark?.(token)
100
+ return token
101
+ }
102
+
67
103
  /**
68
104
  * @param {import('./core.js').ParsedCode} parsed
69
105
  * @param {import('./core.js').DisplayOptions | undefined} options
@@ -74,30 +110,13 @@ function generate(parsed, options) {
74
110
  const markLine = options?.markLine
75
111
 
76
112
  return parsed.lines.map((parsedLine) => {
77
- const line = {
78
- index: parsedLine.index,
79
- value: parsedLine.value,
80
- tokens: parsedLine.tokens,
81
- annotations: parsedLine.annotations,
82
- className: `sh__line${parsedLine.annotations.map(annotation => ` sh__line--${annotation}`).join('')}`,
83
- style: {},
84
- properties: {},
85
- }
86
- markLine?.(line)
113
+ const line = createLine(parsedLine, markLine)
87
114
 
88
115
  return {
89
116
  type: 'element',
90
117
  tagName: 'span',
91
- children: parsedLine.tokens.map(({ type, value }) => {
92
- const extraClassName = cx?.[type]
93
- const token = {
94
- type,
95
- value,
96
- className: `sh__token--${type}${extraClassName ? ` ${extraClassName}` : ''}`,
97
- style: { color: `var(--sh-${type})` },
98
- properties: {},
99
- }
100
- mark?.(token)
118
+ children: parsedLine.tokens.map((parsedToken) => {
119
+ const token = createToken(parsedToken, cx, mark)
101
120
  return {
102
121
  type: 'element',
103
122
  tokenType: token.type,
@@ -119,6 +138,46 @@ function generate(parsed, options) {
119
138
  })
120
139
  }
121
140
 
141
+ /**
142
+ * Render parsed lines directly while preserving generate()'s markup and mutable display hooks.
143
+ * @param {import('./core.js').ParsedCode} parsed
144
+ * @param {import('./core.js').DisplayOptions | undefined} options
145
+ */
146
+ function render(parsed, options) {
147
+ const cx = options?.cx
148
+ const mark = options?.mark
149
+ const markLine = options?.markLine
150
+
151
+ if (!cx && !mark && !markLine) {
152
+ return parsed.lines.map((line) => {
153
+ const className = `sh__line${line.annotations.map(annotation => ` sh__line--${annotation}`).join('')}`
154
+ const children = line.tokens.map(({ type, value }) => (
155
+ `<span class="sh__token--${type}" style="color:var(--sh-${type})">${encode(value)}</span>`
156
+ )).join('')
157
+ return `<span class="${encode(className)}">${children}</span>`
158
+ }).join('\n')
159
+ }
160
+
161
+ return parsed.lines.map((parsedLine) => {
162
+ const line = createLine(parsedLine, markLine)
163
+
164
+ const children = parsedLine.tokens.map((parsedToken) => {
165
+ const token = createToken(parsedToken, cx, mark)
166
+ return `<span ${attributes({
167
+ ...token.properties,
168
+ className: token.className,
169
+ style: token.style,
170
+ })}>${encode(token.value)}</span>`
171
+ }).join('')
172
+
173
+ return `<span ${attributes({
174
+ ...line.properties,
175
+ className: line.className,
176
+ style: line.style,
177
+ })}>${children}</span>`
178
+ }).join('\n')
179
+ }
180
+
122
181
  const entities = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }
123
182
  /** @param {string} value */
124
183
  const encode = (value) => value.replace(/[&<>"']/g, character => entities[character])
@@ -133,17 +192,7 @@ function attributes(values) {
133
192
  return `class="${encode(values.className || '')}"${style ? ` style="${encode(style)}"` : ''}${properties ? ` ${properties}` : ''}`
134
193
  }
135
194
 
136
- /** @param {Array<any>} lines */
137
- function toHtml(lines) {
138
- return lines.map(line => {
139
- const children = line.children.map(token => {
140
- return `<${token.tagName} ${attributes(token.properties)}>${encode(token.children[0].value)}</${token.tagName}>`
141
- }).join('')
142
- return `<${line.tagName} ${attributes(line.properties)}>${children}</${line.tagName}>`
143
- }).join('\n')
144
- }
145
-
146
195
  export {
147
- assemble, encode, generate, SugarHigh, toHtml, TokenTypes, T_BREAK, T_CLASS, T_COMMENT, T_ENTITY, T_IDENTIFIER,
196
+ assemble, encode, generate, render, SugarHigh, TokenTypes, T_BREAK, T_CLASS, T_COMMENT, T_ENTITY, T_IDENTIFIER,
148
197
  T_JSX_LITERALS, T_KEYWORD, T_PROPERTY, T_SIGN, T_SPACE, T_STRING,
149
198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sugar-high",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/huozhi/sugar-high.git",
@@ -40,6 +40,7 @@
40
40
  "scripts": {
41
41
  "test": "vitest",
42
42
  "build": "echo 'package requires no build'",
43
- "benchmark": "node scripts/benchmark.mjs"
43
+ "benchmark": "node scripts/benchmark.mjs",
44
+ "benchmark:large": "node scripts/benchmark-large.mjs"
44
45
  }
45
46
  }