| 2266 | } |
| 2267 | |
| 2268 | function tryParseBraceLikeCat(P: ParseState): TsNode[] | null { |
| 2269 | // {a,b,c} or {} → split into word fragments like tree-sitter does |
| 2270 | if (peek(P.L) !== '{') return null |
| 2271 | const oStart = P.L.b |
| 2272 | advance(P.L) |
| 2273 | const oEnd = P.L.b |
| 2274 | const inner: TsNode[] = [mk(P, 'word', oStart, oEnd, [])] |
| 2275 | while (P.L.i < P.L.len) { |
| 2276 | const bc = peek(P.L) |
| 2277 | // SECURITY: stop at command terminators so `{foo;rm x` splits correctly. |
| 2278 | if ( |
| 2279 | bc === '}' || |
| 2280 | bc === '\n' || |
| 2281 | bc === ';' || |
| 2282 | bc === '|' || |
| 2283 | bc === '&' || |
| 2284 | bc === ' ' || |
| 2285 | bc === '\t' || |
| 2286 | bc === '<' || |
| 2287 | bc === '>' || |
| 2288 | bc === '(' || |
| 2289 | bc === ')' |
| 2290 | ) { |
| 2291 | break |
| 2292 | } |
| 2293 | // `[` and `]` are single-char words: {o[k]} → { o [ k ] } |
| 2294 | if (bc === '[' || bc === ']') { |
| 2295 | const bStart = P.L.b |
| 2296 | advance(P.L) |
| 2297 | inner.push(mk(P, 'word', bStart, P.L.b, [])) |
| 2298 | continue |
| 2299 | } |
| 2300 | const midStart = P.L.b |
| 2301 | while (P.L.i < P.L.len) { |
| 2302 | const mc = peek(P.L) |
| 2303 | if ( |
| 2304 | mc === '}' || |
| 2305 | mc === '\n' || |
| 2306 | mc === ';' || |
| 2307 | mc === '|' || |
| 2308 | mc === '&' || |
| 2309 | mc === ' ' || |
| 2310 | mc === '\t' || |
| 2311 | mc === '<' || |
| 2312 | mc === '>' || |
| 2313 | mc === '(' || |
| 2314 | mc === ')' || |
| 2315 | mc === '[' || |
| 2316 | mc === ']' |
| 2317 | ) { |
| 2318 | break |
| 2319 | } |
| 2320 | advance(P.L) |
| 2321 | } |
| 2322 | const midEnd = P.L.b |
| 2323 | if (midEnd > midStart) { |
| 2324 | const midText = sliceBytes(P, midStart, midEnd) |
| 2325 | const midType = /^-?\d+$/.test(midText) ? 'number' : 'word' |