Skip to content

Commit 68ddbe1

Browse files
committed
Improve Faust doc API retrieval and notes extraction
1 parent 63776ab commit 68ddbe1

3 files changed

Lines changed: 59 additions & 30 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ extracts for each documented symbol:
5858
- `summary`
5959
- `usage`
6060
- `params`
61+
- `notes`
6162
- `io` with `inSignals` / `outSignals` when derivable
6263
- `testCode`
6364
- `references`

scripts/build_faust_doc_index.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@ def parse_doc_body(body_lines: Iterable[str]) -> dict[str, object]:
343343
- free text before any subsection -> summary
344344
- `#### Usage` -> usage string or fenced usage block
345345
- `Where:` -> list of documented parameters
346+
- general notes that appear inside `Where:` after the parameter bullets
347+
-> `notes`
346348
- `#### Test` -> example snippet
347349
- `#### Reference` / `#### References` -> references list
348350
@@ -353,13 +355,20 @@ def parse_doc_body(body_lines: Iterable[str]) -> dict[str, object]:
353355
summary_lines: list[str] = []
354356
usage_buffer: list[str] = []
355357
params: list[dict[str, str]] = []
358+
notes: list[str] = []
356359
references: list[str] = []
357360
test_code: str | None = None
358361

359362
section = "summary"
360363
in_fence = False
361364
fence_buffer: list[str] = []
362365
current_param: dict[str, str] | None = None
366+
current_note_index: int | None = None
367+
368+
def is_general_note_line(text: str) -> bool:
369+
"""Heuristically detect note-like prose inside a `Where:` section."""
370+
371+
return bool(re.match(r"^(note|notes|output|outputs|return|returns|result|results)\b", text, flags=re.IGNORECASE))
363372

364373
def flush_fence() -> None:
365374
"""Commit the current fenced code block into the active logical section."""
@@ -397,6 +406,7 @@ def flush_fence() -> None:
397406
if re.match(r"^where\s*:?\s*$", trimmed, flags=re.IGNORECASE):
398407
section = "where"
399408
current_param = None
409+
current_note_index = None
400410
continue
401411

402412
if trimmed.startswith("```"):
@@ -426,8 +436,15 @@ def flush_fence() -> None:
426436
if match:
427437
current_param = {"name": match.group(1).strip(), "description": match.group(2).strip()}
428438
params.append(current_param)
429-
elif current_param and trimmed and not trimmed.startswith("* "):
439+
current_note_index = None
440+
elif current_note_index is not None and trimmed and not trimmed.startswith("* "):
441+
notes[current_note_index] = f"{notes[current_note_index]} {trimmed}".strip()
442+
elif current_param and trimmed and not trimmed.startswith("* ") and not is_general_note_line(trimmed):
430443
current_param["description"] = f"{current_param['description']} {trimmed}".strip()
444+
elif trimmed and not trimmed.startswith("* "):
445+
notes.append(trimmed)
446+
current_note_index = len(notes) - 1
447+
current_param = None
431448
continue
432449

433450
if section == "reference":
@@ -440,12 +457,16 @@ def flush_fence() -> None:
440457

441458
usage = None
442459
if usage_buffer:
443-
usage = next((line for line in usage_buffer if ":" in line), usage_buffer[0])
460+
if len(usage_buffer) == 1:
461+
usage = usage_buffer[0]
462+
else:
463+
usage = re.sub(r"\s+", " ", " ".join(usage_buffer)).strip()
444464

445465
return {
446466
"summary": " ".join(summary_lines).strip(),
447467
"usage": usage,
448468
"params": params,
469+
"notes": notes,
449470
"testCode": test_code,
450471
"references": references,
451472
}
@@ -569,6 +590,7 @@ def build_index(repo_root: Path, stdlib: Path) -> dict[str, object]:
569590
"summary": body["summary"],
570591
"usage": usage,
571592
"params": body["params"],
593+
"notes": body["notes"],
572594
"io": io,
573595
"testCode": body["testCode"],
574596
"references": body["references"],
@@ -616,6 +638,7 @@ def make_symbol_summary(symbol: dict[str, object]) -> dict[str, object]:
616638
"tags": symbol["tags"],
617639
"source": symbol["source"],
618640
"hasTestCode": bool(symbol.get("testCode")),
641+
"notesCount": len(symbol.get("notes", [])),
619642
"referencesCount": len(symbol.get("references", [])),
620643
}
621644

scripts/faust_doc_api.py

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,23 @@ def get_faust_examples(store: FaustDocStore, symbol_or_module: str, limit: int =
299299
if not key:
300300
raise ValueError("Missing symbolOrModule")
301301

302+
library, symbols = store.get_library_symbols(key)
303+
if library is not None:
304+
examples = []
305+
for symbol in symbols:
306+
if not symbol.get("testCode"):
307+
continue
308+
examples.append(
309+
{
310+
"symbol": symbol.get("qualifiedName"),
311+
"code": symbol.get("testCode"),
312+
"source": symbol.get("source"),
313+
}
314+
)
315+
if len(examples) >= limit:
316+
break
317+
return {"scope": "module", "query": key, "file": library.get("file"), "examples": examples}
318+
302319
found, _ = store.find_symbol(key)
303320
if found is not None:
304321
examples = []
@@ -312,24 +329,7 @@ def get_faust_examples(store: FaustDocStore, symbol_or_module: str, limit: int =
312329
)
313330
return {"scope": "symbol", "query": key, "examples": examples}
314331

315-
library, symbols = store.get_library_symbols(key)
316-
if library is None:
317-
raise ValueError(f"No symbol/module found: {key}")
318-
319-
examples = []
320-
for symbol in symbols:
321-
if not symbol.get("testCode"):
322-
continue
323-
examples.append(
324-
{
325-
"symbol": symbol.get("qualifiedName"),
326-
"code": symbol.get("testCode"),
327-
"source": symbol.get("source"),
328-
}
329-
)
330-
if len(examples) >= limit:
331-
break
332-
return {"scope": "module", "query": key, "file": library.get("file"), "examples": examples}
332+
raise ValueError(f"No symbol/module found: {key}")
333333

334334

335335
def explain_faust_symbol_for_goal(store: FaustDocStore, symbol: str, goal: str) -> dict[str, object]:
@@ -341,23 +341,28 @@ def explain_faust_symbol_for_goal(store: FaustDocStore, symbol: str, goal: str)
341341

342342
goal_text = str(goal or "").strip()
343343
params = found.get("params", [])
344+
notes = found.get("notes", [])
344345
param_hint = (
345346
"Key params: " + "; ".join(f"{param['name']} ({param['description']})" for param in params)
346347
if params
347348
else "No explicit parameter notes found."
348349
)
350+
notes_hint = "Notes: " + " ".join(str(note) for note in notes) if notes else None
349351
usage = f"Usage: {found['usage']}" if found.get("usage") else "Usage not documented."
350-
recommendation = " ".join(
351-
[
352-
f"Use {found['qualifiedName']} when it matches this goal: {goal_text or 'general DSP design'}.",
353-
str(found.get("summary") or "No summary found in comments."),
354-
usage,
355-
param_hint,
356-
"A test snippet is available via get_faust_examples."
357-
if found.get("testCode")
358-
else "No test snippet found.",
359-
]
352+
parts = [
353+
f"Use {found['qualifiedName']} when it matches this goal: {goal_text or 'general DSP design'}.",
354+
str(found.get("summary") or "No summary found in comments."),
355+
usage,
356+
param_hint,
357+
]
358+
if notes_hint:
359+
parts.append(notes_hint)
360+
parts.append(
361+
"A test snippet is available via get_faust_examples."
362+
if found.get("testCode")
363+
else "No test snippet found."
360364
)
365+
recommendation = " ".join(parts)
361366
return {"symbol": found.get("qualifiedName"), "goal": goal_text, "recommendation": recommendation}
362367

363368

0 commit comments

Comments
 (0)