| 256 | } |
| 257 | |
| 258 | export async function tree(input: { cwd: string; limit?: number }) { |
| 259 | log.info("tree", input) |
| 260 | const files = await Array.fromAsync(Ripgrep.files({ cwd: input.cwd })) |
| 261 | interface Node { |
| 262 | path: string[] |
| 263 | children: Node[] |
| 264 | } |
| 265 | |
| 266 | function getPath(node: Node, parts: string[], create: boolean) { |
| 267 | if (parts.length === 0) return node |
| 268 | let current = node |
| 269 | for (const part of parts) { |
| 270 | let existing = current.children.find((x) => x.path.at(-1) === part) |
| 271 | if (!existing) { |
| 272 | if (!create) return |
| 273 | existing = { |
| 274 | path: current.path.concat(part), |
| 275 | children: [], |
| 276 | } |
| 277 | current.children.push(existing) |
| 278 | } |
| 279 | current = existing |
| 280 | } |
| 281 | return current |
| 282 | } |
| 283 | |
| 284 | const root: Node = { |
| 285 | path: [], |
| 286 | children: [], |
| 287 | } |
| 288 | for (const file of files) { |
| 289 | if (file.includes(".arctic")) continue |
| 290 | const parts = file.split(path.sep) |
| 291 | getPath(root, parts, true) |
| 292 | } |
| 293 | |
| 294 | function sort(node: Node) { |
| 295 | node.children.sort((a, b) => { |
| 296 | if (!a.children.length && b.children.length) return 1 |
| 297 | if (!b.children.length && a.children.length) return -1 |
| 298 | return a.path.at(-1)!.localeCompare(b.path.at(-1)!) |
| 299 | }) |
| 300 | for (const child of node.children) { |
| 301 | sort(child) |
| 302 | } |
| 303 | } |
| 304 | sort(root) |
| 305 | |
| 306 | let current = [root] |
| 307 | const result: Node = { |
| 308 | path: [], |
| 309 | children: [], |
| 310 | } |
| 311 | |
| 312 | let processed = 0 |
| 313 | const limit = input.limit ?? 50 |
| 314 | while (current.length > 0) { |
| 315 | const next = [] |