Skip to content

Commit 5f71fef

Browse files
refactor parser.ts for improved clarity and functionality
1 parent efc8311 commit 5f71fef

1 file changed

Lines changed: 48 additions & 11 deletions

File tree

‎src/parser.ts‎

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import path from "node:path";
22
import { PlanItem, ParseOptions } from "./types.js";
33
import {
4-
computeDepth,
54
hasExtension,
65
isBareKnownFile,
76
looksLikeDirToken,
@@ -25,13 +24,13 @@ export function parseToPlan(input: string, opts: ParseOptions): ParseResult {
2524
.map((l) => l.replace(/\s+$/, ""))
2625
.filter((l) => l.trim().length > 0);
2726

28-
// inferRoot: primeira linha é algo como "my-project/" (apenas a barra de fechamento)
27+
// infer base root from a first-line token like "project/"
2928
let baseRoot = rootDir;
3029
if (opts.inferRoot && lines.length > 0) {
31-
const firstContent = sanitizeContent(lines[0]);
32-
if (firstContent.endsWith("/")) {
33-
const token = firstContent.replace(/\/+$/, "");
34-
if (!token.includes("/")) {
30+
const first = sanitizeContent(lines[0]);
31+
if (first.endsWith("/")) {
32+
const token = first.replace(/\/+$/, "");
33+
if (token && !token.includes("/")) {
3534
baseRoot = safeJoin(rootDir, token);
3635
lines = lines.slice(1);
3736
}
@@ -40,10 +39,12 @@ export function parseToPlan(input: string, opts: ParseOptions): ParseResult {
4039

4140
const plan: PlanItem[] = [];
4241
const seen = new Set<string>();
42+
43+
// directory stack by depth
4344
const stack: string[] = [];
4445
stack[0] = baseRoot;
4546

46-
// garanta que o diretório baseRoot apareça no plano
47+
// ensure baseRoot is part of the plan
4748
addDir(baseRoot);
4849

4950
for (const raw of lines) {
@@ -52,14 +53,14 @@ export function parseToPlan(input: string, opts: ParseOptions): ParseResult {
5253
const content0 = sanitizeContent(unclean);
5354
if (!content0) continue;
5455

55-
// caminho absoluto relativo ao baseRoot: "/foo/bar"
56+
// "/foo/bar" relative to baseRoot
5657
if (content0.startsWith("/")) {
5758
const rel = content0.replace(/^\/+/, "");
5859
addAny(rel, 0);
5960
continue;
6061
}
6162

62-
// possui separadores
63+
// token with slashes
6364
if (content0.includes("/")) {
6465
const isDir = content0.endsWith("/");
6566
const rel = isDir ? content0.slice(0, -1) : content0;
@@ -77,7 +78,7 @@ export function parseToPlan(input: string, opts: ParseOptions): ParseResult {
7778
continue;
7879
}
7980

80-
// token "nu" sem barra
81+
// bare token
8182
if (looksLikeDirToken(content0)) {
8283
const parent = stack[depth] ?? baseRoot;
8384
const abs = safeJoin(parent, content0.replace(/\/$/, ""));
@@ -96,14 +97,35 @@ export function parseToPlan(input: string, opts: ParseOptions): ParseResult {
9697
function sanitizeContent(s: string): string {
9798
let out = s.trim();
9899
out = stripBullets(out);
99-
out = out.replace(/^(?:[│├└]?[─-])\s*/, "");
100+
// drop a leading branch marker like "├─ " or "└─ " or "- "
101+
out = out.replace(/^(?:[│├└]\s*)?[─-]\s*/u, "");
100102
out = stripTrailingComments(out);
101103
out = out.trim();
102104
out = out.replace(/\/\s+$/, "/");
103105
out = out.replace(/\s+\((?:opcional|optional)[^)]*\)\s*$/i, "");
104106
return out;
105107
}
106108

109+
function computeDepth(prefix: string): number {
110+
// remove the trailing branch marker (├─ or └─) so it doesn't count as indent
111+
let p = prefix.replace(/[├└]─\s*$/u, "");
112+
// if box pipes exist, depth equals number of pipes
113+
const pipes = (p.match(/│/g) || []).length;
114+
if (pipes > 0) return pipes;
115+
// otherwise, count groups of two spaces
116+
p = p.replace(/[├└─]/g, " ").replace(/\t/g, " ");
117+
let groups = 0, acc = 0;
118+
for (const ch of p) {
119+
if (ch === " ") {
120+
acc++;
121+
if (acc === 2) { groups++; acc = 0; }
122+
} else {
123+
acc = 0;
124+
}
125+
}
126+
return groups;
127+
}
128+
107129
function addAny(rel: string, depth: number) {
108130
const isDir =
109131
rel.endsWith("/") ||
@@ -121,6 +143,21 @@ export function parseToPlan(input: string, opts: ParseOptions): ParseResult {
121143
}
122144

123145
function addDir(abs: string) {
146+
// include all ancestor directories under baseRoot
147+
const rel = path.relative(baseRoot, abs);
148+
if (rel && rel !== ".") {
149+
const parts = rel.split(path.sep).filter(Boolean);
150+
let cur = baseRoot;
151+
for (const seg of parts) {
152+
cur = safeJoin(cur, seg);
153+
pushDir(cur);
154+
}
155+
} else {
156+
pushDir(baseRoot);
157+
}
158+
}
159+
160+
function pushDir(abs: string) {
124161
const key = `d:${abs}`;
125162
if (!seen.has(key)) {
126163
seen.add(key);

0 commit comments

Comments
 (0)