feat(extract): include Verilog header files

Register .vh with the existing Verilog detection and extraction paths,
including incremental and watch coverage.

Refs #3749

AI-Assisted-By: OpenAI Codex
This commit is contained in:
Alex Tumanov committed 2026-10-01 03:35:03 -05:00
1 parent ef4450d9c2
commit 3b74b8ad3d
6 files changed
+64 -2

No files matched your search

+1 -1
View File
@@ -344,7 +344,7 @@ To remove graphify from all platforms at once: `graphify uninstall` (add `--purg
| Type | Extensions |
|------|-----------|
| Code (37 tree-sitter grammars) | `.py .ts .mts .cts .js .jsx .tsx .mjs .go .rs .java .c .cpp .cc .cxx .h .hpp .cu .cuh .metal .rb .cs .kt .kts .scala .php .swift .lua .luau .toc .zig .ps1 .psm1 .psd1 .ex .exs .m .mm .ml .mli .jl .vue .svelte .astro .groovy .gradle .dart .v .sv .svh .sql .f .f90 .f95 .f03 .f08 .pas .pp .dpr .dpk .lpr .inc .dfm .lfm .lpk .sh .bash .json .dm .dme .dmi .dmm .dmf .sln .slnx .csproj .fsproj .vbproj .xaml .razor .cshtml` (`.dm`/`.dme` requires `uv tool install graphifyy[dm]`, `.ml`/`.mli` requires `uv tool install graphifyy[ocaml]`; `.mts`/`.cts` reuse the TypeScript grammar, `.cc`/`.cxx` and CUDA `.cu`/`.cuh` and Metal `.metal` reuse the C++ grammar) |
| Code (37 tree-sitter grammars) | `.py .ts .mts .cts .js .jsx .tsx .mjs .go .rs .java .c .cpp .cc .cxx .h .hpp .cu .cuh .metal .rb .cs .kt .kts .scala .php .swift .lua .luau .toc .zig .ps1 .psm1 .psd1 .ex .exs .m .mm .ml .mli .jl .vue .svelte .astro .groovy .gradle .dart .v .sv .svh .vh .sql .f .f90 .f95 .f03 .f08 .pas .pp .dpr .dpk .lpr .inc .dfm .lfm .lpk .sh .bash .json .dm .dme .dmi .dmm .dmf .sln .slnx .csproj .fsproj .vbproj .xaml .razor .cshtml` (`.dm`/`.dme` requires `uv tool install graphifyy[dm]`, `.ml`/`.mli` requires `uv tool install graphifyy[ocaml]`; `.mts`/`.cts` reuse the TypeScript grammar, `.cc`/`.cxx` and CUDA `.cu`/`.cuh` and Metal `.metal` reuse the C++ grammar) |
| Salesforce Apex | `.cls .trigger` (regex-based; classes, interfaces, enums, methods, triggers, SOQL/DML edges) |
| Terraform / HCL | `.tf .tfvars .hcl` (requires `uv tool install graphifyy[terraform]`) |
| OCaml | `.ml .mli` (requires `uv tool install graphifyy[ocaml]`) |
+1 -1
View File
@@ -44,7 +44,7 @@ _MANIFEST_PATH = str(out_path("manifest.json"))
_MTIME_COARSE_S = 2.0
_MTIME_SUBSECOND_S = 0.05
CODE_EXTENSIONS = {'.py', '.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.ejs', '.ets', '.go', '.rs', '.vb', '.cbl', '.cob', '.cobol', '.cpy', '.java', '.groovy', '.gradle', '.cpp', '.cc', '.cxx', '.c', '.h', '.hpp', '.cu', '.cuh', '.metal', '.rb', '.rake', '.swift', '.kt', '.kts', '.cs', '.scala', '.php', '.lua', '.luau', '.toc', '.zig', '.ps1', '.psm1', '.psd1', '.ex', '.exs', '.m', '.mm', '.ml', '.mli', '.jl', '.vue', '.svelte', '.astro', '.dart', '.v', '.sv', '.svh', '.sql', '.r', '.f', '.F', '.f90', '.F90', '.f95', '.F95', '.f03', '.F03', '.f08', '.F08', '.pas', '.pp', '.dpr', '.dpk', '.lpr', '.inc', '.dfm', '.lfm', '.lpk', '.sh', '.bash', '.json', '.tf', '.tfvars', '.hcl', '.dm', '.dme', '.dmi', '.dmm', '.dmf', '.sln', '.slnx', '.csproj', '.fsproj', '.vbproj', '.xaml', '.razor', '.cshtml', '.cls', '.trigger', '.lisp', '.cl', '.lsp', '.asd', '.robot', '.resource', '.sol', '.erl', '.hrl', '.escript'}
CODE_EXTENSIONS = {'.py', '.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.ejs', '.ets', '.go', '.rs', '.vb', '.cbl', '.cob', '.cobol', '.cpy', '.java', '.groovy', '.gradle', '.cpp', '.cc', '.cxx', '.c', '.h', '.hpp', '.cu', '.cuh', '.metal', '.rb', '.rake', '.swift', '.kt', '.kts', '.cs', '.scala', '.php', '.lua', '.luau', '.toc', '.zig', '.ps1', '.psm1', '.psd1', '.ex', '.exs', '.m', '.mm', '.ml', '.mli', '.jl', '.vue', '.svelte', '.astro', '.dart', '.v', '.sv', '.svh', '.vh', '.sql', '.r', '.f', '.F', '.f90', '.F90', '.f95', '.F95', '.f03', '.F03', '.f08', '.F08', '.pas', '.pp', '.dpr', '.dpk', '.lpr', '.inc', '.dfm', '.lfm', '.lpk', '.sh', '.bash', '.json', '.tf', '.tfvars', '.hcl', '.dm', '.dme', '.dmi', '.dmm', '.dmf', '.sln', '.slnx', '.csproj', '.fsproj', '.vbproj', '.xaml', '.razor', '.cshtml', '.cls', '.trigger', '.lisp', '.cl', '.lsp', '.asd', '.robot', '.resource', '.sol', '.erl', '.hrl', '.escript'}
DOC_EXTENSIONS = {'.md', '.mdx', '.qmd', '.skill', '.txt', '.rst', '.html', '.yaml', '.yml'}
PAPER_EXTENSIONS = {'.pdf'}
IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg'}
+1
View File
@@ -6767,6 +6767,7 @@ _DISPATCH: dict[str, Any] = {
".v": extract_verilog,
".sv": extract_verilog,
".svh": extract_verilog,
".vh": extract_verilog,
".sql": extract_sql,
".md": extract_markdown,
".mdx": extract_markdown,
+26
View File
@@ -34,6 +34,32 @@ def test_classify_python():
def test_classify_typescript():
assert classify_file(Path("bar.ts")) == FileType.CODE
@pytest.mark.parametrize("suffix", [".vh", ".VH"])
def test_detect_verilog_headers(tmp_path, suffix):
header = tmp_path / f"defs{suffix}"
header.write_text("`define WIDTH 8\n", encoding="utf-8")
assert classify_file(header) == FileType.CODE
result = detect(tmp_path)
assert header.resolve() in {Path(path).resolve() for path in result["files"]["code"]}
def test_detect_incremental_requeues_changed_verilog_header(tmp_path):
header = tmp_path / "defs.vh"
header.write_text("`define WIDTH 8\n", encoding="utf-8")
manifest_path = tmp_path / "graphify-out" / "manifest.json"
full = detect(tmp_path)
save_manifest(full["files"], manifest_path, root=tmp_path, kind="ast")
warm = detect_incremental(tmp_path, manifest_path, kind="ast")
assert warm["new_total"] == 0
header.write_text("`define WIDTH 16\n", encoding="utf-8")
changed = detect_incremental(tmp_path, manifest_path, kind="ast")
assert header.resolve() in {
Path(path).resolve() for path in changed["new_files"]["code"]
}
def test_classify_powershell_module():
# #1315: .psm1 modules were never indexed (CODE_EXTENSIONS gap).
assert classify_file(Path("Utils.psm1")) == FileType.CODE
+27
View File
@@ -3875,6 +3875,33 @@ def test_apex_no_dangling_edges():
# -- SystemVerilog -------------------------------------------------------------
@pytest.mark.parametrize("suffix", [".v", ".sv", ".svh", ".vh"])
def test_verilog_family_dispatch(suffix):
from graphify.extract import _get_extractor
assert _get_extractor(Path(f"defs{suffix}")) is extract_verilog
@pytest.mark.parametrize("with_module", [False, True])
def test_verilog_header_collection_and_extraction(tmp_path, with_module):
from graphify.extract import collect_files, extract
header = tmp_path / "defs.vh"
source = "`define WIDTH 8\n"
if with_module:
source += "module helper(input wire a, output wire b);\nassign b = a;\nendmodule\n"
header.write_text(source, encoding="utf-8")
paths = collect_files(tmp_path)
assert header in paths
result = extract(paths, root=tmp_path, cache_root=tmp_path, parallel=False)
assert header.name in {node["label"] for node in result["nodes"]}
if with_module:
assert (header.name, "helper") in _edge_labels(result, "defines")
else:
assert {node["label"] for node in result["nodes"]} == {header.name}
def test_systemverilog_no_error():
r = extract_verilog(FIXTURES / "sample.sv")
assert "error" not in r
+8
View File
@@ -47,6 +47,14 @@ def test_watched_extensions_includes_code():
assert ".go" in _WATCHED_EXTENSIONS
assert ".rs" in _WATCHED_EXTENSIONS
def test_verilog_header_triggers_code_rebuild_without_llm(tmp_path):
header = tmp_path / "defs.vh"
header.write_text("`define WIDTH 8\n", encoding="utf-8")
assert ".vh" in _WATCHED_EXTENSIONS
assert _batch_triggers_rebuild([header]) is True
assert _batch_needs_llm_flag([header]) is False
def test_watched_extensions_includes_docs():
assert ".md" in _WATCHED_EXTENSIONS
assert ".txt" in _WATCHED_EXTENSIONS