Skip to content

Add Nix Language Support#1048

Open
eyildirir wants to merge 5 commits into
safishamsi:v8from
eyildirir:eyildirir/add-nix-support
Open

Add Nix Language Support#1048
eyildirir wants to merge 5 commits into
safishamsi:v8from
eyildirir:eyildirir/add-nix-support

Conversation

@eyildirir
Copy link
Copy Markdown

@eyildirir eyildirir commented May 27, 2026

Issue: #1049

This PR adds native Nix (.nix) parser and AST extractor support to graphify. This enables the tool to discover .nix files, parse them using tree-sitter-nix, extract code symbols (let-bindings, attributes, recursive attribute sets), and map relationships (such as relative imports and list-based module declarations) into the knowledge graph.

Key Changes

  1. Parser & Extractor:

    • Added tree-sitter-nix to project dependencies in pyproject.toml.
    • Implemented AST traversal logic in extract_nix in graphify/extract.py to parse Nix expressions (bindings, import statements, recursive rec { ... } sets, and list attributes like imports = [ ... ]).
  2. AST Build, Scoping, and Presentation:

    • Added Nix suffix checks and mapped them to the "nix" language family in detect.py, build.py, analyze.py, symbol_resolution.py, and callflow_html.py to prevent false-positive cross-language references and format Nix module labels and HTML descriptions correctly.
  3. Verification:

    • Added automated tests in test_nix_extraction in tests/test_languages.py.

Manual Verification

Tested against a couple of nix based projects I already knew deep and wide.

To manually verify the changes against a Nix configuration project:

  1. Install dependencies and the package:
    pip install -e .
  2. Run extraction against a Nix project:
    graphify extract /path/to/nix/project
  3. Inspect output:
    • Verify that communities are correctly grouped and the generated graphify-out/graph.html and GRAPH_REPORT.md show the extracted Nix attributes and import edges cleanly.

@safishamsi
Copy link
Copy Markdown
Owner

Thanks for the Nix support! The extractor logic is solid — the _get_import_argument traversal handles Nix's curried import ./dep.nix correctly and the imports = [ ... ] list case is a nice catch. A few things need addressing before merge:

Use _extract_generic + LanguageConfig

Every other language in graphify follows the _extract_generic(path, config: LanguageConfig) pattern — see extract_r, extract_lua, extract_zig for simple examples. The custom function/node/edge helper duplication inside extract_nix is ~60 lines that _extract_generic already provides. Please refactor to a _NIX_CONFIG = LanguageConfig(...) with the appropriate tree-sitter node type mappings and use _extract_generic to drive the walk. The custom import resolution logic (_add_import_edge, _handle_imports_list) can live as extra_walk on the config.

Add a fixture file

Tests use tmp_path inline. Add tests/fixtures/sample.nix instead — same pattern as sample.lua, sample.r, etc. The fixture should exercise bindings, an import, and at least one imports = [...] list so the tests are reproducible without constructing ad-hoc files.

Minor: duplicate branch in _add_import_edge

if path_str.startswith("./") or path_str.startswith("../"):
    resolved_path = (path.parent / path_str).resolve()
else:
    resolved_path = (path.parent / path_str).resolve()  # identical

Both arms do the same thing — the if/else can be collapsed to a single line.

tree-sitter-nix as a hard dependency

Adding it to dependencies means every pip install graphifyy downloads the package regardless of whether the user has any .nix files. Follow the existing soft-import pattern used by other optional grammars: catch ImportError and return {"nodes": [], "edges": [], "error": "tree-sitter-nix not installed"} (which the dispatcher already handles).

Once these are addressed this is ready to land.

eyildirir added 2 commits May 28, 2026 00:52
Add a fixture file for nix,
dedupe branch in `_add_import_edge`
Make `tree-sitter-nix` a soft dependency.
@eyildirir
Copy link
Copy Markdown
Author

eyildirir commented May 28, 2026

Thanks for the Nix support! The extractor logic is solid — the _get_import_argument traversal handles Nix's curried import ./dep.nix correctly and the imports = [ ... ] list case is a nice catch. A few things need addressing before merge:

Use _extract_generic + LanguageConfig

Every other language in graphify follows the _extract_generic(path, config: LanguageConfig) pattern — see extract_r, extract_lua, extract_zig for simple examples. The custom function/node/edge helper duplication inside extract_nix is ~60 lines that _extract_generic already provides. Please refactor to a _NIX_CONFIG = LanguageConfig(...) with the appropriate tree-sitter node type mappings and use _extract_generic to drive the walk. The custom import resolution logic (_add_import_edge, _handle_imports_list) can live as extra_walk on the config.

Add a fixture file

Tests use tmp_path inline. Add tests/fixtures/sample.nix instead — same pattern as sample.lua, sample.r, etc. The fixture should exercise bindings, an import, and at least one imports = [...] list so the tests are reproducible without constructing ad-hoc files.

Minor: duplicate branch in _add_import_edge

if path_str.startswith("./") or path_str.startswith("../"):
    resolved_path = (path.parent / path_str).resolve()
else:
    resolved_path = (path.parent / path_str).resolve()  # identical

Both arms do the same thing — the if/else can be collapsed to a single line.

tree-sitter-nix as a hard dependency

Adding it to dependencies means every pip install graphifyy downloads the package regardless of whether the user has any .nix files. Follow the existing soft-import pattern used by other optional grammars: catch ImportError and return {"nodes": [], "edges": [], "error": "tree-sitter-nix not installed"} (which the dispatcher already handles).

Once these are addressed this is ready to land.

Addressed all 4 issues raised, following the example of how Lua, Swift and C# is handled. Deconflicted with HEAD as well. All tests pass again.

Ready for a second pass @safishamsi.

Thank you for your time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants