♻️ refactor(tsl-syntax): make query output compact and safe

This commit is contained in:
csh
2026-07-13 09:16:58 +08:00
parent 0ace66571f
commit 6728ed070e
2 changed files with 126 additions and 35 deletions
+55 -26
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse
import difflib
import json
import re
import sys
import unicodedata
@@ -561,40 +562,67 @@ def query_sections(
return QueryResult(query=query, mode=mode, matches=matches, prelude=prelude)
def render_result(result: QueryResult) -> str:
def _logical_source(section: Section) -> str:
return f"references/{section.page.name}"
def _plain_text_summary(body: str, limit: int = 180) -> str:
without_fences = FENCED_CODE_RE.sub(" ", body)
without_links = re.sub(
r"!?\[([^\]]*)\]\([^)]+\)", lambda match: match.group(1), without_fences
)
without_inline_code = INLINE_CODE_RE.sub(lambda match: match.group(1), without_links)
content_lines = []
for line in without_inline_code.splitlines():
stripped = line.strip()
if not stripped or stripped.startswith(("#", IDENTITY_PREFIX, BLOCK_DESCRIPTION_PREFIX)):
continue
content_lines.append(stripped.lstrip("-* "))
summary = re.sub(r"\s+", " ", " ".join(content_lines)).strip()
if len(summary) <= limit:
return summary
return summary[: limit - 1].rstrip() + ""
def render_candidates(result: QueryResult) -> str:
lines = [
"# TSL Syntax Lookup",
"# TSL Syntax Candidates",
"",
f"Mode: `{result.mode}`",
f"Query: {result.query}",
f"Query: {json.dumps(result.query, ensure_ascii=False)}",
]
if result.prelude:
lines.extend(["", "## Required context"])
for section in result.prelude:
lines.extend(
[
"",
f"Section ID: `{section.id}`",
f"Source: `{section.page.as_posix()}`",
"",
section.body.rstrip(),
]
)
for index, match in enumerate(result.matches, start=1):
candidates = [
(section, 0, True, (0, 0, 0, 0, 0)) for section in result.prelude
] + [
(match.section, match.score, False, match.priority) for match in result.matches
]
for index, (section, score, required, priority) in enumerate(candidates, start=1):
lines.extend(
[
"",
f"## Match {index}",
f"## Candidate {index}",
"",
f"Score: {match.score}",
f"Section ID: `{match.section.id}`",
f"Source: `{match.section.page.as_posix()}`",
"",
match.section.body.rstrip(),
f"Score: {score}",
f"Required: {'yes' if required else 'no'}",
f"Section ID: `{section.id}`",
f"Source: `{_logical_source(section)}`",
f"Heading: `{' > '.join(section.heading_path)}`",
f"Why: `rank={','.join(str(value) for value in priority)}`",
f"Summary: {_plain_text_summary(section.body)}",
]
)
lines.extend(["", "## Narrower section IDs"])
lines.extend(f"- `{match.section.id}`" for match in result.matches)
return "\n".join(lines).rstrip() + "\n"
def render_section(section: Section) -> str:
lines = [
"# TSL Syntax Section",
"",
f"Section ID: `{section.id}`",
f"Source: `{_logical_source(section)}`",
"",
section.body.rstrip(),
]
return "\n".join(lines).rstrip() + "\n"
@@ -724,13 +752,14 @@ def main(argv: list[str] | None = None) -> int:
for candidate in _nearest_section_ids(args.section, sections):
print(f"- {candidate}", file=sys.stderr)
return 2
result = QueryResult("", "section", [QueryMatch(section, 0)], [])
print(render_section(section), end="")
return 0
else:
result = query_sections(args.query, args.mode, args.limit, args.references_dir)
if not result.matches:
print("no matching sections", file=sys.stderr)
return 2
print(render_result(result), end="")
print(render_candidates(result), end="")
return 0