From 2c2579319d5217f04cfade3a1563f7fd9803b9f8 Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Mon, 11 Aug 2025 16:20:14 +0200 Subject: [PATCH] update hover feature --- server/src/tools/proc_docs.py | 41 +++++++++-- server/tests/python_tests/test_proc_docs.py | 75 +++++++++++++++++++++ 2 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 server/tests/python_tests/test_proc_docs.py diff --git a/server/src/tools/proc_docs.py b/server/src/tools/proc_docs.py index 8dbd21a..c603ab1 100644 --- a/server/src/tools/proc_docs.py +++ b/server/src/tools/proc_docs.py @@ -42,7 +42,7 @@ def extract_doc_block_above(lines: List[str], start_line_index: int) -> str | No # Reverse to original order and strip comment prefixes doc_lines.reverse() - cleaned = [_strip_comment_prefix(l) for l in doc_lines] + cleaned = [_strip_comment_prefix(line_text) for line_text in doc_lines] # Simple tag -> markdown conversions for nicer rendering md_lines: List[str] = [] @@ -55,12 +55,45 @@ def extract_doc_block_above(lines: List[str], start_line_index: int) -> str | No "": "### Internal Example", } + in_example = False + code_block_open = False + + def close_code_block_if_open(): + nonlocal code_block_open + if code_block_open: + md_lines.append("```") + code_block_open = False + for line in cleaned: stripped = line.strip() + # Convert tags to headings and manage example sections if stripped in tag_map: - md_lines.append(tag_map[stripped]) - else: - md_lines.append(line) + # If we hit any new tag, close a pending code block + close_code_block_if_open() + heading = tag_map[stripped] + md_lines.append(heading) + in_example = heading in ("### Example", "### Internal Example") + continue + + if in_example: + low = stripped.lower() + if low.startswith("code:"): + # Open code block if needed and append the code content + code_text = line.split(":", 1)[1].strip() + if not code_block_open: + md_lines.append("```tcl") + code_block_open = True + md_lines.append(code_text) + continue + # Keep name/desc lines as regular text outside code + if low.startswith("name:") or low.startswith("desc:"): + md_lines.append(line) + continue + + md_lines.append(line) + + # Close any dangling code fence at the end of the block + close_code_block_if_open() return "\n".join(md_lines).rstrip() diff --git a/server/tests/python_tests/test_proc_docs.py b/server/tests/python_tests/test_proc_docs.py new file mode 100644 index 0000000..e94397a --- /dev/null +++ b/server/tests/python_tests/test_proc_docs.py @@ -0,0 +1,75 @@ +import sys +from pathlib import Path + +# Ensure server/src is on sys.path for imports +THIS_DIR = Path(__file__).parent +SRC_DIR = THIS_DIR.parent.parent / "src" +if str(SRC_DIR) not in sys.path: + sys.path.insert(0, str(SRC_DIR)) + +import lsprotocol.types as lsp # type: ignore +from tools.parser import CustomParser +from tools.proc_docs import build_proc_docs + + +def test_example_is_marked_as_tcl_code_block(): + source_lines = [ + "#____________________________________________________________________________________________", + "# ", + "# This procedure creates a new directory if it does not exist.", + "# ", + "# directory", + "#\tThe full pathname of the directory to be created.", + "# ", + "# 0 - directory created or already exists", + "# 1 - error", + "# ", + "# name: Example 1", + "# code: LIB_FH_create_directory \"C:/Temp/Test\"", + "# desc: If error = 0, the directory is created.", + "proc LIB_FH_create_directory {directory} {", + " return 0", + "}", + ] + source = "\n".join(source_lines) + + tree = CustomParser().parse(source) + docs = build_proc_docs(tree, source) + + assert "LIB_FH_create_directory" in docs + md = docs["LIB_FH_create_directory"] + + # Headings preserved + assert "### Documentation" in md + assert "### Arguments" in md + assert "### Return value" in md + assert "### Example" in md + + # Code fence with tcl language hint and the code line present + assert "```tcl" in md + assert "LIB_FH_create_directory \"C:/Temp/Test\"" in md + assert md.strip().endswith("```") + + +def test_internal_example_is_marked_as_tcl_code_block(): + source_lines = [ + "# ", + "# Helper utility", + "# ", + "# code: puts \"hello\"", + "proc helper {} {", + " return", + "}", + ] + source = "\n".join(source_lines) + + tree = CustomParser().parse(source) + docs = build_proc_docs(tree, source) + + assert "helper" in docs + md = docs["helper"] + + assert "### Internal Documentation" in md + assert "### Internal Example" in md + assert "```tcl" in md and "puts \"hello\"" in md and md.strip().endswith("```") +