[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay
5

Configure Feed

Select the types of activity you want to include in your feed.

deno lint

Nate Moore (Jul 2, 2026, 12:16 AM EDT) c87edbcd fe65a270

+47 -47
+47 -47
tasks/gen-wcwidth.ts
··· 8 8 // BMP coarse filter: a 64-byte bitmap (1 bit per 128-codepoint block) gates 9 9 // the binary search. Clean blocks return width 1 without touching the table. 10 10 11 - const UNICODE_BASE = "https://www.unicode.org/Public/16.0.0/ucd"; 11 + let UNICODE_BASE = "https://www.unicode.org/Public/16.0.0/ucd"; 12 12 13 13 interface Interval { 14 14 start: number; ··· 20 20 } 21 21 22 22 async function fetchText(path: string): Promise<string> { 23 - const url = `${UNICODE_BASE}/${path}`; 23 + let url = `${UNICODE_BASE}/${path}`; 24 24 console.error(`Fetching ${url} …`); 25 - const response = await fetch(url); 25 + let response = await fetch(url); 26 26 if (!response.ok) { 27 27 throw new Error(`HTTP ${response.status} fetching ${url}`); 28 28 } ··· 31 31 32 32 function parseCodepointRange(token: string): Interval { 33 33 if (token.includes("..")) { 34 - const [lo, hi] = token.split("..").map((s) => parseInt(s, 16)); 34 + let [lo, hi] = token.split("..").map((s) => parseInt(s, 16)); 35 35 return { start: lo, end: hi }; 36 36 } 37 - const cp = parseInt(token, 16); 37 + let cp = parseInt(token, 16); 38 38 return { start: cp, end: cp }; 39 39 } 40 40 ··· 43 43 * `XXXX..YYYY ; Category # …` and returns intervals matching `category`. 44 44 */ 45 45 function parseGeneralCategory(text: string, category: string): Interval[] { 46 - const intervals: Interval[] = []; 47 - for (const line of text.split("\n")) { 48 - const content = line.split("#")[0].trim(); 46 + let intervals: Interval[] = []; 47 + for (let line of text.split("\n")) { 48 + let content = line.split("#")[0].trim(); 49 49 if (!content) continue; 50 - const [range, cat] = content.split(";").map((s) => s.trim()); 50 + let [range, cat] = content.split(";").map((s) => s.trim()); 51 51 if (cat !== category) continue; 52 52 intervals.push(parseCodepointRange(range)); 53 53 } ··· 59 59 * `XXXX ; Property_Name # …` and returns intervals matching `property`. 60 60 */ 61 61 function parseDerivedProperty(text: string, property: string): Interval[] { 62 - const intervals: Interval[] = []; 63 - for (const line of text.split("\n")) { 64 - const content = line.split("#")[0].trim(); 62 + let intervals: Interval[] = []; 63 + for (let line of text.split("\n")) { 64 + let content = line.split("#")[0].trim(); 65 65 if (!content) continue; 66 - const [range, prop] = content.split(";").map((s) => s.trim()); 66 + let [range, prop] = content.split(";").map((s) => s.trim()); 67 67 if (prop !== property) continue; 68 68 intervals.push(parseCodepointRange(range)); 69 69 } ··· 75 75 * and returns intervals for Wide (W) and Fullwidth (F) codepoints. 76 76 */ 77 77 function parseWideEastAsian(text: string): Interval[] { 78 - const intervals: Interval[] = []; 79 - for (const line of text.split("\n")) { 80 - const content = line.split("#")[0].trim(); 78 + let intervals: Interval[] = []; 79 + for (let line of text.split("\n")) { 80 + let content = line.split("#")[0].trim(); 81 81 if (!content) continue; 82 - const [range, width] = content.split(";").map((s) => s.trim()); 82 + let [range, width] = content.split(";").map((s) => s.trim()); 83 83 if (width !== "W" && width !== "F") continue; 84 84 intervals.push(parseCodepointRange(range)); 85 85 } ··· 89 89 function mergeIntervals(intervals: Interval[]): Interval[] { 90 90 if (intervals.length === 0) return []; 91 91 intervals.sort((a, b) => a.start - b.start); 92 - const merged: Interval[] = [{ ...intervals[0] }]; 92 + let merged: Interval[] = [{ ...intervals[0] }]; 93 93 for (let index = 1; index < intervals.length; index++) { 94 - const current = merged[merged.length - 1]; 95 - const next = intervals[index]; 94 + let current = merged[merged.length - 1]; 95 + let next = intervals[index]; 96 96 if (next.start <= current.end + 1) { 97 97 current.end = Math.max(current.end, next.end); 98 98 } else { ··· 109 109 */ 110 110 function subtractIntervals(base: Interval[], mask: Interval[]): Interval[] { 111 111 let result = [...base]; 112 - for (const m of mask) { 113 - const next: Interval[] = []; 114 - for (const b of result) { 112 + for (let m of mask) { 113 + let next: Interval[] = []; 114 + for (let b of result) { 115 115 if (m.end < b.start || m.start > b.end) { 116 116 next.push(b); 117 117 } else { ··· 126 126 127 127 function assertNoAdjacentRanges(intervals: Interval[], label: string): void { 128 128 for (let index = 1; index < intervals.length; index++) { 129 - const previous = intervals[index - 1]; 130 - const current = intervals[index]; 129 + let previous = intervals[index - 1]; 130 + let current = intervals[index]; 131 131 if (current.start <= previous.end + 1) { 132 132 throw new Error( 133 133 `${label}: adjacent ranges at index ${index}: ` + ··· 155 155 } 156 156 157 157 function formatUint32Array(values: number[], indent = " "): string { 158 - const lines: string[] = []; 158 + let lines: string[] = []; 159 159 for (let index = 0; index < values.length; index += 8) { 160 - const chunk = values.slice(index, index + 8); 160 + let chunk = values.slice(index, index + 8); 161 161 lines.push(indent + chunk.map(formatUint32Hex).join(", ") + ","); 162 162 } 163 163 return lines.join("\n"); 164 164 } 165 165 166 166 function formatUint16Array(values: number[], indent = " "): string { 167 - const lines: string[] = []; 167 + let lines: string[] = []; 168 168 for (let index = 0; index < values.length; index += 8) { 169 - const chunk = values.slice(index, index + 8); 169 + let chunk = values.slice(index, index + 8); 170 170 lines.push(indent + chunk.map(formatUint16Hex).join(", ") + ","); 171 171 } 172 172 return lines.join("\n"); 173 173 } 174 174 175 175 function formatUint8Array(values: number[], indent = " "): string { 176 - const lines: string[] = []; 176 + let lines: string[] = []; 177 177 for (let index = 0; index < values.length; index += 8) { 178 - const chunk = values.slice(index, index + 8); 178 + let chunk = values.slice(index, index + 8); 179 179 lines.push(indent + chunk.map(formatUint8Hex).join(", ") + ","); 180 180 } 181 181 return lines.join("\n"); 182 182 } 183 183 184 - const [derivedCategoryText, derivedCorePropsText, eastAsianWidthText] = 184 + let [derivedCategoryText, derivedCorePropsText, eastAsianWidthText] = 185 185 await Promise.all([ 186 186 fetchText("extracted/DerivedGeneralCategory.txt"), 187 187 fetchText("DerivedCoreProperties.txt"), 188 188 fetchText("EastAsianWidth.txt"), 189 189 ]); 190 190 191 - const nonspacingMarks = parseGeneralCategory(derivedCategoryText, "Mn"); 191 + let nonspacingMarks = parseGeneralCategory(derivedCategoryText, "Mn"); 192 192 const enclosingMarks = parseGeneralCategory(derivedCategoryText, "Me"); 193 193 const defaultIgnorables = parseDerivedProperty( 194 194 derivedCorePropsText, ··· 211 211 ); 212 212 assertNoAdjacentRanges(combiningIntervals, "combining"); 213 213 214 - const wideIntervals = mergeIntervals(parseWideEastAsian(eastAsianWidthText)); 214 + let wideIntervals = mergeIntervals(parseWideEastAsian(eastAsianWidthText)); 215 215 assertNoAdjacentRanges(wideIntervals, "wide"); 216 216 217 217 // Strip any codepoints that are in both tables — combining takes priority. ··· 225 225 ].sort((a, b) => a.start - b.start); 226 226 227 227 for (let index = 1; index < allSpecialIntervals.length; index++) { 228 - const prev = allSpecialIntervals[index - 1]; 229 - const curr = allSpecialIntervals[index]; 228 + let prev = allSpecialIntervals[index - 1]; 229 + let curr = allSpecialIntervals[index]; 230 230 if (curr.start <= prev.end) { 231 231 throw new Error( 232 232 `combining/wide overlap at 0x${curr.start.toString(16)} ` + ··· 235 235 } 236 236 } 237 237 238 - const allRanges = allSpecialIntervals.map((i) => ({ 238 + let allRanges = allSpecialIntervals.map((i) => ({ 239 239 start: i.start, 240 240 count: i.end - i.start, 241 241 width: i.width, 242 242 })); 243 243 244 - for (const range of allRanges) { 244 + for (let range of allRanges) { 245 245 if (range.start > 0x1fffff) { 246 246 throw new Error( 247 247 `Range start 0x${ ··· 258 258 const smallRanges = allRanges.filter((range) => range.count <= 1023); 259 259 const largeRanges = allRanges.filter((range) => range.count > 1023); 260 260 261 - const smallPacked = smallRanges.map( 261 + let smallPacked = smallRanges.map( 262 262 (range) => 263 263 (range.start << 11) | (range.count << 1) | (range.width === 2 ? 1 : 0), 264 264 ); ··· 271 271 } 272 272 } 273 273 274 - const largeStarts = largeRanges.map((range) => range.start); 274 + let largeStarts = largeRanges.map((range) => range.start); 275 275 const largeCounts = largeRanges.map((range) => range.count); 276 276 const largeWidths = largeRanges.map((range) => range.width); 277 277 ··· 280 280 const bmpFilter = new Uint32Array(16); 281 281 for (const range of allRanges) { 282 282 if (range.start > 0xffff) continue; 283 - const endCp = Math.min(range.start + range.count, 0xffff); 284 - const startBlock = range.start >> 7; 285 - const endBlock = endCp >> 7; 283 + let endCp = Math.min(range.start + range.count, 0xffff); 284 + let startBlock = range.start >> 7; 285 + let endBlock = endCp >> 7; 286 286 for (let block = startBlock; block <= endBlock; block++) { 287 287 bmpFilter[block >> 5] |= 1 << (block & 31); 288 288 } ··· 296 296 } 297 297 } 298 298 299 - const tableBytes = 16 * 4 + // bmp_filter 299 + let tableBytes = 16 * 4 + // bmp_filter 300 300 smallPacked.length * 4 + 301 301 largeStarts.length * 4 + 302 302 largeCounts.length * 2 + ··· 307 307 console.error(`BMP dirty blocks: ${dirtyBlocks} / 512`); 308 308 console.error(`Table data: ${tableBytes} bytes`); 309 309 310 - const date = new Date().toISOString().slice(0, 10); 310 + let date = new Date().toISOString().slice(0, 10); 311 311 312 - const output = `\ 312 + let output = `\ 313 313 /* wcwidth.c - Unicode character width lookup 314 314 * Unicode 16.0 - generated by tasks/gen-wcwidth.ts on ${date} 315 315 * ··· 413 413 `; 414 414 415 415 if (Deno.args.includes("--check")) { 416 - const existing = await Deno.readTextFile("src/wcwidth.c"); 416 + let existing = await Deno.readTextFile("src/wcwidth.c"); 417 417 if (existing !== output) { 418 418 console.error("src/wcwidth.c is out of date — run: deno task gen-wcwidth"); 419 419 Deno.exit(1);