source dump of claude code
at main 308 lines 6.4 kB view raw
1/** 2 * SGR (Select Graphic Rendition) Parser 3 * 4 * Parses SGR parameters and applies them to a TextStyle. 5 * Handles both semicolon (;) and colon (:) separated parameters. 6 */ 7 8import type { NamedColor, TextStyle, UnderlineStyle } from './types.js' 9import { defaultStyle } from './types.js' 10 11const NAMED_COLORS: NamedColor[] = [ 12 'black', 13 'red', 14 'green', 15 'yellow', 16 'blue', 17 'magenta', 18 'cyan', 19 'white', 20 'brightBlack', 21 'brightRed', 22 'brightGreen', 23 'brightYellow', 24 'brightBlue', 25 'brightMagenta', 26 'brightCyan', 27 'brightWhite', 28] 29 30const UNDERLINE_STYLES: UnderlineStyle[] = [ 31 'none', 32 'single', 33 'double', 34 'curly', 35 'dotted', 36 'dashed', 37] 38 39type Param = { value: number | null; subparams: number[]; colon: boolean } 40 41function parseParams(str: string): Param[] { 42 if (str === '') return [{ value: 0, subparams: [], colon: false }] 43 44 const result: Param[] = [] 45 let current: Param = { value: null, subparams: [], colon: false } 46 let num = '' 47 let inSub = false 48 49 for (let i = 0; i <= str.length; i++) { 50 const c = str[i] 51 if (c === ';' || c === undefined) { 52 const n = num === '' ? null : parseInt(num, 10) 53 if (inSub) { 54 if (n !== null) current.subparams.push(n) 55 } else { 56 current.value = n 57 } 58 result.push(current) 59 current = { value: null, subparams: [], colon: false } 60 num = '' 61 inSub = false 62 } else if (c === ':') { 63 const n = num === '' ? null : parseInt(num, 10) 64 if (!inSub) { 65 current.value = n 66 current.colon = true 67 inSub = true 68 } else { 69 if (n !== null) current.subparams.push(n) 70 } 71 num = '' 72 } else if (c >= '0' && c <= '9') { 73 num += c 74 } 75 } 76 return result 77} 78 79function parseExtendedColor( 80 params: Param[], 81 idx: number, 82): { r: number; g: number; b: number } | { index: number } | null { 83 const p = params[idx] 84 if (!p) return null 85 86 if (p.colon && p.subparams.length >= 1) { 87 if (p.subparams[0] === 5 && p.subparams.length >= 2) { 88 return { index: p.subparams[1]! } 89 } 90 if (p.subparams[0] === 2 && p.subparams.length >= 4) { 91 const off = p.subparams.length >= 5 ? 1 : 0 92 return { 93 r: p.subparams[1 + off]!, 94 g: p.subparams[2 + off]!, 95 b: p.subparams[3 + off]!, 96 } 97 } 98 } 99 100 const next = params[idx + 1] 101 if (!next) return null 102 if ( 103 next.value === 5 && 104 params[idx + 2]?.value !== null && 105 params[idx + 2]?.value !== undefined 106 ) { 107 return { index: params[idx + 2]!.value! } 108 } 109 if (next.value === 2) { 110 const r = params[idx + 2]?.value 111 const g = params[idx + 3]?.value 112 const b = params[idx + 4]?.value 113 if ( 114 r !== null && 115 r !== undefined && 116 g !== null && 117 g !== undefined && 118 b !== null && 119 b !== undefined 120 ) { 121 return { r, g, b } 122 } 123 } 124 return null 125} 126 127export function applySGR(paramStr: string, style: TextStyle): TextStyle { 128 const params = parseParams(paramStr) 129 let s = { ...style } 130 let i = 0 131 132 while (i < params.length) { 133 const p = params[i]! 134 const code = p.value ?? 0 135 136 if (code === 0) { 137 s = defaultStyle() 138 i++ 139 continue 140 } 141 if (code === 1) { 142 s.bold = true 143 i++ 144 continue 145 } 146 if (code === 2) { 147 s.dim = true 148 i++ 149 continue 150 } 151 if (code === 3) { 152 s.italic = true 153 i++ 154 continue 155 } 156 if (code === 4) { 157 s.underline = p.colon 158 ? (UNDERLINE_STYLES[p.subparams[0]!] ?? 'single') 159 : 'single' 160 i++ 161 continue 162 } 163 if (code === 5 || code === 6) { 164 s.blink = true 165 i++ 166 continue 167 } 168 if (code === 7) { 169 s.inverse = true 170 i++ 171 continue 172 } 173 if (code === 8) { 174 s.hidden = true 175 i++ 176 continue 177 } 178 if (code === 9) { 179 s.strikethrough = true 180 i++ 181 continue 182 } 183 if (code === 21) { 184 s.underline = 'double' 185 i++ 186 continue 187 } 188 if (code === 22) { 189 s.bold = false 190 s.dim = false 191 i++ 192 continue 193 } 194 if (code === 23) { 195 s.italic = false 196 i++ 197 continue 198 } 199 if (code === 24) { 200 s.underline = 'none' 201 i++ 202 continue 203 } 204 if (code === 25) { 205 s.blink = false 206 i++ 207 continue 208 } 209 if (code === 27) { 210 s.inverse = false 211 i++ 212 continue 213 } 214 if (code === 28) { 215 s.hidden = false 216 i++ 217 continue 218 } 219 if (code === 29) { 220 s.strikethrough = false 221 i++ 222 continue 223 } 224 if (code === 53) { 225 s.overline = true 226 i++ 227 continue 228 } 229 if (code === 55) { 230 s.overline = false 231 i++ 232 continue 233 } 234 235 if (code >= 30 && code <= 37) { 236 s.fg = { type: 'named', name: NAMED_COLORS[code - 30]! } 237 i++ 238 continue 239 } 240 if (code === 39) { 241 s.fg = { type: 'default' } 242 i++ 243 continue 244 } 245 if (code >= 40 && code <= 47) { 246 s.bg = { type: 'named', name: NAMED_COLORS[code - 40]! } 247 i++ 248 continue 249 } 250 if (code === 49) { 251 s.bg = { type: 'default' } 252 i++ 253 continue 254 } 255 if (code >= 90 && code <= 97) { 256 s.fg = { type: 'named', name: NAMED_COLORS[code - 90 + 8]! } 257 i++ 258 continue 259 } 260 if (code >= 100 && code <= 107) { 261 s.bg = { type: 'named', name: NAMED_COLORS[code - 100 + 8]! } 262 i++ 263 continue 264 } 265 266 if (code === 38) { 267 const c = parseExtendedColor(params, i) 268 if (c) { 269 s.fg = 270 'index' in c 271 ? { type: 'indexed', index: c.index } 272 : { type: 'rgb', ...c } 273 i += p.colon ? 1 : 'index' in c ? 3 : 5 274 continue 275 } 276 } 277 if (code === 48) { 278 const c = parseExtendedColor(params, i) 279 if (c) { 280 s.bg = 281 'index' in c 282 ? { type: 'indexed', index: c.index } 283 : { type: 'rgb', ...c } 284 i += p.colon ? 1 : 'index' in c ? 3 : 5 285 continue 286 } 287 } 288 if (code === 58) { 289 const c = parseExtendedColor(params, i) 290 if (c) { 291 s.underlineColor = 292 'index' in c 293 ? { type: 'indexed', index: c.index } 294 : { type: 'rgb', ...c } 295 i += p.colon ? 1 : 'index' in c ? 3 : 5 296 continue 297 } 298 } 299 if (code === 59) { 300 s.underlineColor = { type: 'default' } 301 i++ 302 continue 303 } 304 305 i++ 306 } 307 return s 308}