Skip to content

Index Liquid templates - #878

Merged
2 commits merged into
tirth8205:stagingfrom
yzxcj797:fix/348-liquid-indexing
Nov 9, 2026
Merged

2 commits merged into
tirth8205:stagingfrom
yzxcj797:fix/348-liquid-indexing

Conversation

@yzxcj797

Copy link
Copy Markdown
Contributor

Summary

.liquid files were not recognized by detect_language, so full builds excluded them and file summaries returned zero nodes.

This adds a structural, regex-based Liquid parser that indexes:

  • a File node for every template;
  • Variable nodes for assign declarations, including whitespace liquid blocks;
  • Output nodes for non-empty {{ ... }} expressions;
  • REFERENCES edges for static render and include targets.

Liquid comments are masked while preserving byte offsets and line numbers, so directives inside comments are not indexed.

Fixes #348.

Testing

  • Added regressions for:
    • ordinary assign, output, render, and comment masking;
    • whitespace {% liquid ... %} blocks.
  • pytest tests/test_liquid.py -q — 2 passed
  • ruff check code_review_graph/parser.py tests/test_liquid.py — passes
  • git diff --check — passes

@github-actions

github-actions Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

code-review-graph review

Overall risk: 0.40 (MEDIUM) — 8 changed function(s)/class(es), 17 affected flow(s), 5 test gap(s)

Risk-scored changes

Risk Level Symbol Location Tested
0.40 medium code_review_graph/parser.py::CodeParser.line_at code_review_graph/parser.py:3612 no
0.35 low code_review_graph/parser.py::CodeParser._mask_liquid_comments code_review_graph/parser.py:3580 no
0.30 low code_review_graph/parser.py::CodeParser code_review_graph/parser.py:2936 yes
0.30 low code_review_graph/parser.py::CodeParser.replace_comment code_review_graph/parser.py:3582 no
0.25 low tests/test_liquid.py::test_liquid_file_has_file_assignment_and_render_nodes tests/test_liquid.py:8 (test)
0.10 low code_review_graph/parser.py::CodeParser._extract_bytes code_review_graph/parser.py:3200 no
0.10 low code_review_graph/parser.py::CodeParser._parse_liquid code_review_graph/parser.py:3590 no
0.05 low tests/test_liquid.py::test_whitespace_liquid_tag_is_indexed tests/test_liquid.py:44 (test)

Affected execution flows

  • visit_FunctionDef — criticality 0.41, 55 node(s) across 1 file(s)
  • visit_AsyncFunctionDef — criticality 0.41, 55 node(s) across 1 file(s)
  • visit_ClassDef — criticality 0.41, 54 node(s) across 1 file(s)
  • visit_If — criticality 0.36, 3 node(s) across 1 file(s)
  • repo_relative_path — criticality 0.36, 9 node(s) across 1 file(s)
  • ...and 12 more affected flow(s)

Test gaps

  • code_review_graph/parser.py::CodeParser._extract_bytes (code_review_graph/parser.py:3200)
  • code_review_graph/parser.py::CodeParser.replace_comment (code_review_graph/parser.py:3582)
  • code_review_graph/parser.py::CodeParser._mask_liquid_comments (code_review_graph/parser.py:3580)
  • code_review_graph/parser.py::CodeParser._parse_liquid (code_review_graph/parser.py:3590)
  • code_review_graph/parser.py::CodeParser.line_at (code_review_graph/parser.py:3612)

Token savings: this graph-backed report used ~174,355 fewer tokens (~89%) than reading every changed file in full (estimated, chars/4 approximation).


Powered by code-review-graph — local-first analysis; no code leaves the CI runner.

@tirth8205

Copy link
Copy Markdown
Owner

Outside this sweep's mission of demonstrated coding-session token savings: this adds a new Liquid parser. Bare template references, raw-block handling and a complete cross-file impact workflow need evidence before that language expansion is included.

@tirth8205
tirth8205 changed the base branch from main to staging September 15, 2026 13:11
@tirth8205

tirth8205 commented Sep 15, 2026 •

Copy link
Copy Markdown
Owner

Checks pass and the code looks right. Missing: tests for the edge cases it introduces.

  • Test that {% raw %}...{% endraw %} content is not indexed (currently produces false-positive Output/Variable/REFERENCES nodes; the fix would mask raw blocks the same way comments are masked).
  • Test that '#' comment lines inside {% liquid %} blocks are not indexed (currently 'assign'/'render' on '#' lines produce false positives).
  • Test for {% include 'x' %} and for double-quoted render/include targets (include is never exercised).
  • Test for empty file and for empty {{ }} output being skipped.
  • Test for trim markers ({%- -%}, {{- -}}) and multi-line {{ ... }} output line ranges.

Every new branch in the code should have a test, including the failure branch. Disagree with any of these? Say so here first.

PRs now target staging, not main. Yours was retargeted already, so nothing to do there.

@tirth8205 tirth8205 added the needs-tests Code is fine, edge cases are untested label Sep 15, 2026
@tirth8205

Copy link
Copy Markdown
Owner

{% raw %} blocks are indexed as live code, and so are # comment lines inside a liquid tag. Both reproduce on the head commit, and both are among the five tests I asked for on 15 September. Those tests are not in the branch and there was no reply here. The only commit since is the staging merge on 18 September, and tests/test_liquid.py still has exactly two tests.

A file containing only a raw block:

{% raw %}
{% assign should_not_exist = 1 %}
{{ should_not_be_output }}
{% render 'not-a-real-snippet' %}
{% endraw %}

yields a Variable should_not_exist, an Output output_3:should_not_be_output, and a REFERENCES edge to not-a-real-snippet. None of it should reach the graph. _mask_liquid_comments masks {% comment %} and nothing else.

Same for # lines inside a liquid tag:

{% liquid
  # assign ghost_var = 1
  # render 'ghost-snippet'
  assign real_var = 2 %}

Variables come out as ['ghost_var', 'real_var'] with a phantom REFERENCES edge to ghost-snippet.

Render targets still do not resolve. I built Shopify Dawn on this branch. All 127 new liquid REFERENCES edges are stored with target_resolution='unresolved' against a bare string such as card-product or cart-drawer. snippets/card-product.liquid is a File node in the same graph with 0 inbound edges, even though 7 templates render it. "Who renders this snippet" returns nothing, so the cross-file impact this is for is not there yet.

Output names embed the line number, so qualified_name is <file>::output_{line}:<expr>. I inserted one {% comment %} line at the top of sections/image-banner.liquid and ran code-review-graph update. 30 of that file's 44 nodes were destroyed and recreated under new names. No other node kind behaves this way, and memory.py, graph_diff snapshots and the embeddings index all key on qualified_name. The same build emits 4046 liquid nodes over Dawn but stores only 3836. 210 collapse across 156 names through UNIQUE(qualified_name), for example card-product.liquid::ratio six times, so line_start lands on an arbitrary occurrence.

Gates are genuinely clean: 4237 passed, 0 failed, ruff clean, mypy clean. The table diff over Dawn is purely additive. 0 node names lost, 0 edges lost, Class 51 to 51, Function 429 to 429, CALLS 2522 to 2522. Nothing existing is touched or respelled.

One cost to weigh while fixing the rest. 3445 of 4352 nodes are Output nodes whose name is raw expression text. FTS hits for product go from 67 to 1151, and 85% of those are Output nodes. That works against the token-efficiency goal on exactly the repos this targets.

To merge:

  • Mask {% raw %} blocks and # lines the way comments are masked, each with a test.
  • Resolve render and include targets to the snippets/<name>.liquid File node so inbound edges exist.
  • Drop the line number from Output names and make the key unique without it.
  • Add the remaining three tests from my earlier list, plus tests/fixtures/sample.liquid.

@tirth8205

Copy link
Copy Markdown
Owner

Thank you for this PR.

I tested it merged with the latest staging on a small Liquid repo.

Before I can merge it, please fix these:

  1. {% raw %} blocks are read as live code. So are # lines inside {% liquid %}. The parser skips only {% comment %} (code_review_graph/parser.py:3580). Example: {% raw %}{% assign ghost = 1 %}{% endraw %} gives a Variable ghost. Please skip both, each with a test.
  2. render and include targets are saved as bare names like card (parser.py:3652). So snippets/card.liquid gets no inbound edges. Its impact radius is empty. Please link each target to its snippets/<name>.liquid File node.
  3. Output names hold the line number (parser.py:3622). I added one line at the top of a file. Every Output node below it got a new name. Please make names unique without the line number.
  4. Two assign ratio lines in one file become one node. The first one is lost (parser.py:3640).
  5. Some tests are missing. Please test include, double quotes, an empty file and empty {{ }}. Please also test trim markers and multi-line output. Please add tests/fixtures/sample.liquid too.

After you push the fix, I will test it again.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The parser still extracts false variables, outputs, and dependencies from quoted values, inline comments, and raw blocks, polluting the code-review graph.

3 open issues
What changed in this PR

Adds structural Liquid template indexing to the code-review graph, addressing #348 by recognizing .liquid files and extracting graph content without a dedicated Tree-sitter grammar.

Changes:

  • Registers Liquid file detection and a regex-based parser.
  • Extracts file, assignment, and output nodes plus static render/include references, while masking block comments.
  • Adds regression tests for ordinary tags and multiline liquid blocks.
File Description
tests/​test_liquid.py Tests Liquid detection, extraction, block-comment masking, and multiline blocks.
code_review_graph/​parser.py Adds Liquid recognition, parser dispatch, and structural extraction.

Review effort: Balanced

r"""(?<!@)@(extends|include|component|livewire)\s*\(\s*(['"])([^'"]+)\2\s*\)""",
)
_LIQUID_COMMENT_RE = re.compile(
r"\{%-?\s*comment\s*-?%\}.*?\{%-?\s*endcomment\s*-?%\}",
for character in match.group(0)
)

return cls._LIQUID_COMMENT_RE.sub(replace_comment, text)
Comment on lines +3635 to +3636
for segment in segments:
for assign in self._LIQUID_ASSIGN_RE.finditer(segment):
@tirth8205 tirth8205 closed this pull request by merging all changes into tirth8205:staging in d01c1fd Nov 9, 2026
HengYangDS pushed a commit to HengYangDS/code-review-graph that referenced this pull request Nov 9, 2026
HengYangDS pushed a commit to HengYangDS/code-review-graph that referenced this pull request Nov 9, 2026
The review on tirth8205#878 listed what a real theme showed:

- `raw` blocks and `#` lines inside `{% liquid %}` read as live code. So did
  inline comment tags, `doc`, `schema`, `javascript`, `stylesheet` and quoted
  text. Only the first word of a statement is now its directive.
- `render` and `include` left bare names, so a snippet had no inbound edges and
  an empty impact radius. They now link to `snippets/<name>.liquid` with
  `IMPORTS_FROM`, as Blade includes do, and the lookup stays inside the repo.
- Output names held the line number, so one new line renamed every output below
  it. A second `assign` to a name also overwrote the first. A repeated name is
  now one node that lists every line, and file nodes contain their children.
- Output nodes took all ten top hits for "product" on Dawn, so search leaves them
  out unless asked for with `kind="Output"`.

On Shopify's Dawn: 124 of 124 render and include statements link to a file,
`importers_of` on `card-product` finds its 6 templates, adding a line above
`image-banner` renames 0 of 33 nodes, and 2,780 nodes emitted are 2,780 stored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-tests Code is fine, edge cases are untested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

not picking .liquid files

3 participants