Skip to content

Conversation

@joshuadavidthomas
Copy link
Owner

@joshuadavidthomas joshuadavidthomas commented Nov 4, 2025

Summary by CodeRabbit

  • New Features

    • TagSpecs upgraded to v0.6.0 with hierarchical, library-based organization; libraries can override built-ins.
  • Deprecated

    • v0.4.0 flat TagSpecs format deprecated; scheduled removal in v5.2.7.
  • Compatibility

    • Legacy v0.4.0 input still accepted with deprecation warning and automatic migration to v0.6.0.
  • Behavior Changes

    • Many block end-tags are now required, affecting snippets, diagnostics, and block-closing.
  • Documentation

    • New TagSpecs docs, migration guide, and updated default wording for tagspecs.

@codspeed-hq
Copy link

codspeed-hq bot commented Nov 4, 2025

CodSpeed Performance Report

Merging #350 will not alter performance

Comparing tagspecs-v0.5.0 (5dc32ad) with main (179aea7)

Summary

✅ 20 untouched

@joshuadavidthomas
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 4, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Nov 4, 2025

Walkthrough

Upgrades TagSpecs to v0.6.0 hierarchical format with libraries, adds a deprecated v0.4.0 legacy compatibility converter and deserializer, changes Settings.tagspecs to a single TagSpecDef, expands TagSpecs types/exports, updates semantic conversions and block end-tag semantics, removes old TAGSPECS.md, and refreshes docs, changelog, and tooling config.

Changes

Cohort / File(s) Change Summary
Changelog & Docs
CHANGELOG.md, docs/configuration.md, docs/tagspecs.md
Updated changelog to v0.6.0 TagSpecs, added deprecation note and migration link for v0.4.0, changed default tagspecs description, and added comprehensive v0.6.0 TagSpecs documentation and migration guidance.
Deleted legacy docs
crates/djls-conf/TAGSPECS.md
Removed the old flat-format TAGSPECS.md documentation file.
djls-conf manifest
crates/djls-conf/Cargo.toml
Added workspace dependencies: serde_json and tracing.
djls-semantic manifest
crates/djls-semantic/Cargo.toml
Added dev-dependency serde_json.
Core TagSpecs types
crates/djls-conf/src/tagspecs.rs
Introduced v0.6.0 root TagSpecDef and related public types/enums (TagLibraryDef, TagDef, TagTypeDef, EndTagDef, IntermediateTagDef, PositionDef, TagArgDef, ArgTypeDef, ArgKindDef) with serde defaults.
Legacy conversion module
crates/djls-conf/src/tagspecs/legacy.rs
New deprecated compatibility module modelling legacy v0.4.0 types and convert_legacy_tagspecs(...) to transform flat specs into v0.6.0 TagSpecDef; includes conversion helpers and tests.
Settings & exports
crates/djls-conf/src/lib.rs
Settings.tagspecs changed from Vec<TagSpecDef> to TagSpecDef with custom deserialize_tagspecs accepting v0.6.0 or legacy v0.4.0 (emits deprecation); overrides path updated; new public exports added and log_dir() introduced; accessor signature updated.
Semantic integration
crates/djls-semantic/src/templatetags/specs.rs
Now iterates settings.tagspecs().libraries, converts (TagDef, module)TagSpec, updates end/intermediate/arg mapping (extracts Choice from extra), and adapts tests.
Blocks & grammar
crates/djls-semantic/src/blocks/*
End-tag semantics switched from optional to required: EndMeta/API renamed/adjusted; block finalization and diagnostics now use required logic.
Builtins & snippets
crates/djls-semantic/src/templatetags/builtins.rs, crates/djls-ide/src/snippets.rs
Many built-in end tags now marked required; snippet generation and related tests adjusted to use end_tag.required.
Docs tooling
.mkdocs.yml
Added pymdownx.tabbed markdown extension with options.
Workspace lint config
Cargo.toml
Added [workspace.lints.rust] with deprecated = "allow" to suppress deprecation lints for legacy tagspecs.

Sequence Diagram(s)

sequenceDiagram
    participant Config as Config Source
    participant Deser as Custom Deserializer
    participant Compat as Legacy Compat
    participant Settings as Settings

    Config->>Deser: Load tagspecs (v0.6.0 hierarchical or v0.4.0 flat)
    Deser->>Deser: Detect format
    alt v0.6.0 (hierarchical)
        Deser->>Settings: Parse to TagSpecDef
        Note over Settings: TagSpecDef contains libraries
    else v0.4.0 (legacy flat)
        Deser->>Compat: convert_legacy_tagspecs(legacy)
        Compat->>Compat: Group by module, transform tags/args, store choices in extra
        Compat-->>Deser: Return TagSpecDef
        Deser-->>Settings: Emit deprecation warning and provide TagSpecDef
    end
    Settings->>Settings: Apply overrides via tagspecs.libraries
Loading
sequenceDiagram
    participant Semantic as Semantic Engine
    participant Settings as Settings
    participant Lib as Library Loop
    participant Tag as Tag Loop
    participant Convert as TagDef→TagSpec

    Semantic->>Settings: settings.tagspecs()
    Settings-->>Semantic: TagSpecDef (with libraries)
    loop for each library
        Semantic->>Lib: iterate library
        loop for each tag in library
            Lib->>Tag: pass (TagDef, module)
            Tag->>Convert: From<(TagDef, module)>
            Convert->>Convert: derive end_tag by TagType (synthesize when needed)
            Convert->>Convert: map intermediates & args
            Convert->>Convert: extract Choice from extra metadata if present
            Convert-->>Tag: TagSpec
            Tag->>Semantic: register/override TagSpec
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Areas to focus on:
    • crates/djls-conf/src/tagspecs/legacy.rs: correctness of legacy→v0.6.0 mapping, choice metadata handling, and tests.
    • crates/djls-conf/src/lib.rs: deserialize_tagspecs implementation, deprecation warning behavior, and override path changes.
    • crates/djls-semantic/src/templatetags/specs.rs: TagDef→TagSpec conversion (end-tag synthesis, arg kind mapping).
    • Blocks/grammar changes: EndMeta renames and use-sites impacting diagnostics and parsing.

Poem

🐰 A hop through specs both old and new,
Libraries grouped, choices tucked too.
A gentle deprecate, a tidy convert,
Tests race forward — no line left hurt.
Carrots, code, and tags in view 🥕

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'migrate to TagSpecs v0.6.0 spec' clearly and specifically summarizes the main change in the changeset, which is a comprehensive migration of the TagSpecs format from v0.4.0 through v0.5.0 to the new v0.6.0 hierarchical specification.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch tagspecs-v0.5.0

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec0fb09 and 5dc32ad.

📒 Files selected for processing (1)
  • crates/djls-conf/src/lib.rs (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
crates/djls-conf/src/lib.rs (1)
crates/djls-conf/src/tagspecs/legacy.rs (1)
  • convert_legacy_tagspecs (117-144)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Python 3.12, Django 6.0a1 (ubuntu-latest)
  • GitHub Check: Python 3.14, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django main (ubuntu-latest)
  • GitHub Check: Python 3.14, Django 6.0a1 (ubuntu-latest)
  • GitHub Check: Python 3.14, Django main (ubuntu-latest)
  • GitHub Check: Python 3.14, Django 4.2 (ubuntu-latest)
  • GitHub Check: Python 3.13, Django 6.0a1 (ubuntu-latest)
  • GitHub Check: Python 3.10, Django 4.2 (ubuntu-latest)
  • GitHub Check: Python 3.14, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.13, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.11, Django 4.2 (ubuntu-latest)
  • GitHub Check: Python 3.13, Django 4.2 (ubuntu-latest)
  • GitHub Check: Python 3.11, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.11, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.10, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.10, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django 4.2 (ubuntu-latest)
  • GitHub Check: benchmarks
🔇 Additional comments (7)
crates/djls-conf/src/lib.rs (7)

16-16: LGTM! New exports support v0.6.0 hierarchical format.

The additional type exports (ArgKindDef, PositionDef, TagDef, TagLibraryDef, TagTypeDef) and Deserializer import are appropriate for the TagSpecs v0.6.0 migration, providing users access to the new hierarchical structure.

Also applies to: 21-21, 25-25, 27-28, 30-30


36-52: LGTM! Well-implemented utility function.

The log_dir() function provides a sensible fallback strategy and proper error context when creating directories.


81-106: Verify migration guide URL is accessible.

The deprecation warning URL at line 98 appears to have been updated from the previous dead link, but please verify that https://djls.joshthomas.dev/tagspecs/#migration-from-v040 is accessible and contains the migration guide. The past review suggested a different URL format.

The deserializer implementation is solid: it tries the v0.6.0 format first, falls back to legacy v0.4.0 with an appropriate warning, and provides a clear error message if neither succeeds.

Minor note: If the v0.6.0 deserialization fails due to a field type mismatch or validation error (rather than wrong format), that specific error is swallowed when attempting the legacy fallback. The final generic error message may be less helpful in such cases. This is acceptable for a deprecation path, but consider logging the v0.6.0 parse error if debugging config issues becomes difficult.


74-75: LGTM! Settings changes align with v0.6.0 hierarchical structure.

The changes to store a single TagSpecDef (instead of Vec<TagSpecDef>) and the updated override merge logic correctly reflect the new hierarchical format where libraries group tags by module. The all-or-nothing override behavior is consistent with how other settings are handled.

Also applies to: 124-126, 200-202


544-689: LGTM! Tests correctly validate the v0.6.0 hierarchical structure.

The test updates properly exercise the new format with libraries containing tags, and correctly use the new ArgKindDef enum for argument type validation.


789-843: LGTM! Comprehensive coverage of all argument kinds.

This test validates all seven ArgKindDef variants (literal, variable, any, syntax, assignment, modifier, choice), ensuring the new type system is working correctly.


846-997: LGTM! Thorough legacy format conversion testing.

The deprecation tests comprehensively validate the v0.4.0 to v0.6.0 migration path, including:

  • Flat format conversion to hierarchical structure
  • Grouping multiple tags by module into libraries
  • Field mapping transformations (optional → required, type → kind)
  • Choice args with extra metadata
  • Intermediate tags and end tags

As noted in the comment at line 845, remember to remove these tests in v5.2.7 when the legacy format support is dropped.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 179aea7 and 43153d4.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • CHANGELOG.md (1 hunks)
  • crates/djls-conf/Cargo.toml (1 hunks)
  • crates/djls-conf/TAGSPECS.md (0 hunks)
  • crates/djls-conf/src/lib.rs (9 hunks)
  • crates/djls-conf/src/tagspecs.rs (2 hunks)
  • crates/djls-conf/src/tagspecs/legacy.rs (1 hunks)
  • crates/djls-semantic/Cargo.toml (1 hunks)
  • crates/djls-semantic/src/templatetags/specs.rs (10 hunks)
  • docs/configuration.md (1 hunks)
  • docs/tagspecs.md (1 hunks)
💤 Files with no reviewable changes (1)
  • crates/djls-conf/TAGSPECS.md
🔇 Additional comments (24)
crates/djls-conf/Cargo.toml (1)

12-12: LGTM!

The addition of serde_json and tracing workspace dependencies appropriately supports the TagSpecs v0.5.0 migration, enabling JSON serialization for the new hierarchical format and structured logging for deprecation warnings.

Also applies to: 15-15

docs/configuration.md (2)

164-164: LGTM!

The updated default description is clearer and more user-friendly than the previous "[]" notation.


168-169: Verify the migration guide link path.

Similar to the CHANGELOG, this references ../crates/djls-conf/TAGSPECS.md#migration-from-v040. Please ensure this file exists at that location or update the link to point to tagspecs.md in the same directory if that's the intended target.

The same verification script from the CHANGELOG comment applies here.

crates/djls-semantic/Cargo.toml (1)

22-22: LGTM!

Adding serde_json as a dev-dependency appropriately supports JSON-based test fixtures for the new TagSpecs format.

docs/tagspecs.md (1)

1-339: Excellent comprehensive documentation!

This is well-structured TagSpecs documentation with clear examples, migration guidance, and a deprecation timeline. The migration section (lines 172-339) provides detailed examples for common patterns.

However, note that this file is located at docs/tagspecs.md, but other files in this PR reference crates/djls-conf/TAGSPECS.md. Please ensure all references point to the correct location.

crates/djls-semantic/src/templatetags/specs.rs (7)

2-2: LGTM!

The Borrowed as B alias simplifies the synthesized end tag creation on lines 196-197.


158-168: LGTM!

The updated conversion correctly iterates through the new hierarchical structure (tagspec_doc.librarieslibrary.tags) and constructs TagSpecs using the tuple-based From implementation with module information.


340-383: Verify the Choice argument extra metadata parsing.

The Choice variant extraction (lines 361-380) relies on:

  1. The extra field containing a HashMap
  2. The "choices" key being present
  3. The value being a JSON array of strings

Consider:

  • Error handling: If the extra metadata is malformed or missing required fields, the code silently falls back to an empty choices list (unwrap_or_default()). This could hide configuration errors.
  • Type safety: The parsing uses runtime JSON value inspection rather than typed deserialization.

Should malformed Choice arguments (missing or invalid extra.choices) generate a warning or diagnostic rather than silently defaulting to empty choices?


396-396: LGTM!

The required/optional inversion is correctly implemented and well-documented with an inline comment. The v0.5.0 spec uses required while the runtime uses optional, so !value.required is the correct mapping.


758-861: LGTM!

The test coverage for conversion functions is comprehensive:

  • Variable, Choice, Literal argument conversions
  • End tag conversion with required/optional inversion
  • Intermediate tag conversion
  • Full TagDef → TagSpec conversion with module

The tests correctly verify the new v0.5.0 types and field mappings.


864-949: LGTM!

The integration test comprehensively verifies:

  • Built-in specs are preserved
  • User-defined tags from [[tagspecs.libraries]] are loaded correctly
  • Module, end tag, intermediates, and args are properly converted
  • User specs can override built-in specs (the if tag override test)
  • Required/optional inversion works correctly in the full pipeline

The TOML fixture uses the new v0.5.0 hierarchical format, confirming the entire conversion path works end-to-end.


187-227: Let me verify the actual tag configurations and Django's standard practices:

Automatic end tag synthesis for Block tags is correctly implemented and follows established Django conventions.

The implementation is sound. Django's block template tags use the format {% block name %}...{% endblock %}, following the endX naming pattern consistently across all built-in tags.

The code's synthesis logic is defensive—it only generates a default end tag when none is explicitly provided, and explicit definitions override the default. The existing codebase shows all Block tags already include explicit end definitions (e.g., "endblock", "endcustom", "endmytag"), confirming the endX convention is universal in the codebase.

No Django or third-party tags violate this naming pattern, and the implementation aligns with v0.5.0 spec expectations.

crates/djls-conf/src/tagspecs/legacy.rs (4)

19-97: LGTM! Legacy structures are well-defined.

The legacy data structures accurately model the v0.4.0 flat format with appropriate field names, defaults, and serde attributes. The use of #[serde(untagged)] for LegacyArgTypeDef correctly handles the Simple vs Choice variants.


99-214: Conversion logic is sound and well-structured.

The conversion functions properly handle:

  • Grouping tags by module into libraries
  • Inverting optional to required for end tags
  • Mapping legacy argument types to new kinds (String/Variable → Variable, Expression/VarArgs → Any)
  • Storing Choice values in extra metadata per v0.5.0 spec

The hardcoded defaults (version="0.5.0", engine="django", arg_type=Both) are appropriate for legacy migration.


216-420: LGTM! Comprehensive test coverage.

The tests thoroughly validate:

  • Tag type detection (standalone vs block)
  • End tag required/optional inversion
  • Argument kind mappings
  • Choice metadata storage
  • Module-based grouping
  • Intermediate tag conversion

1-6: Inconsistency in deprecation timeline confirmed between files.

The deprecation timeline inconsistency is verified. crates/djls-conf/src/tagspecs/legacy.rs (line 1) specifies removal in v5.2.7, while crates/djls-conf/src/tagspecs.rs (line 5) specifies removal in v5.2.2. These conflicting versions must be aligned to avoid confusion about the actual deprecation schedule. Determine which timeline is authoritative and update both deprecation comments accordingly.

crates/djls-conf/src/lib.rs (5)

36-52: LGTM! Well-implemented log directory function.

The log_dir function properly:

  • Uses XDG cache directory when available
  • Falls back to /tmp gracefully
  • Creates the directory if needed
  • Provides clear error context

The new public exports appropriately expand the API surface for the v0.5.0 hierarchical format.


80-110: LGTM! Robust deserializer with proper deprecation handling.

The custom deserializer correctly:

  • Prioritizes the new v0.5.0 format (optimal path)
  • Falls back to legacy v0.4.0 with a clear deprecation warning
  • Includes a migration guide link in the warning
  • Returns a descriptive error if both formats fail

74-75: LGTM! Settings structure properly migrated to v0.5.0.

The changes correctly:

  • Use TagSpecDef as the root type (instead of Vec<TagSpecDef>)
  • Wire in the custom deserializer with deprecation support
  • Check overrides.tagspecs.libraries for non-empty (appropriate for hierarchical structure)
  • Update the accessor return type to &TagSpecDef

Also applies to: 128-129, 204-206


543-847: LGTM! Thorough test coverage for v0.5.0 format.

The updated tests properly validate:

  • Hierarchical structure (libraries → tags → end/intermediates/args)
  • All field types and nesting levels
  • All ArgKindDef variants
  • Integration with other settings

The TOML syntax correctly reflects the new schema.


849-1002: LGTM! Comprehensive legacy format test coverage.

The legacy tests thoroughly validate backward compatibility:

  • Flat array to hierarchical conversion
  • Module-based grouping
  • Optional→required inversion for end tags
  • Choice argument conversion with extra metadata
  • Multiple tags per module handling
crates/djls-conf/src/tagspecs.rs (3)

8-68: LGTM! Well-designed hierarchical structures.

The root structures (TagSpecDef, TagLibraryDef, TagDef) properly model the v0.5.0 spec with:

  • Clear hierarchical organization (root → libraries → tags)
  • Extensibility via extra fields
  • Proper serde defaults and field renaming
  • Comprehensive field documentation

70-181: LGTM! Comprehensive and well-structured type definitions.

The supporting structures and enums effectively model:

  • Tag classification (TagTypeDef: Block/Loader/Standalone)
  • End tag behavior with required field
  • Intermediate tag constraints (min/max/position)
  • Argument typing (positional/keyword/both) and semantic kinds
  • Position constraints (Any/Last)

The separation of arg_type (positional vs keyword) and kind (semantic role) provides clear, orthogonal classification.


183-201: LGTM! Default functions provide sensible values.

The default functions correctly return:

  • Version "0.5.0" (current spec version)
  • Engine "django" (appropriate default)
  • Boolean true (for required fields)
  • PositionDef::Any (most permissive)
  • ArgTypeDef::Both (most flexible)

These defaults align well with the spec's intended behavior.

@joshuadavidthomas joshuadavidthomas marked this pull request as ready for review November 4, 2025 14:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/djls-conf/src/tagspecs/legacy.rs (1)

96-98: Consider reusing default_true from parent module.

This function duplicates the same helper already defined in the parent tagspecs module (lines 190-192). You can import it from the parent module instead.

Apply this diff to remove the duplication:

+use super::default_true;
+
-fn default_true() -> bool {
-    true
-}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0980b36 and 2a710db.

📒 Files selected for processing (1)
  • crates/djls-conf/src/tagspecs/legacy.rs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
crates/djls-conf/src/tagspecs/legacy.rs (1)
crates/djls-conf/src/tagspecs.rs (1)
  • default_true (191-193)
🪛 GitHub Actions: lint
crates/djls-conf/src/tagspecs/legacy.rs

[warning] 5-5: Diff in legacy.rs:5 indicates formatting changes suggested by rustfmt (code style issue).


[error] 5-5: cargo +nightly fmt --all -- --check failed. Rust code is not properly formatted according to rustfmt. Run 'cargo +nightly fmt' to fix formatting.

🪛 GitHub Check: rustfmt
crates/djls-conf/src/tagspecs/legacy.rs

[warning] 5-5:
Diff in /home/runner/work/django-language-server/django-language-server/crates/djls-conf/src/tagspecs/legacy.rs

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Python 3.13, Django main (ubuntu-latest)
  • GitHub Check: Python 3.14, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.14, Django main (ubuntu-latest)
  • GitHub Check: Python 3.14, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.10, Django 4.2 (ubuntu-latest)
  • GitHub Check: Python 3.13, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.11, Django 4.2 (ubuntu-latest)
  • GitHub Check: Python 3.13, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django main (ubuntu-latest)
  • GitHub Check: Python 3.10, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.13, Django 4.2 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.13, Django 6.0a1 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django 6.0a1 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django 4.2 (ubuntu-latest)
  • GitHub Check: Python 3.11, Django 5.1 (ubuntu-latest)
  • GitHub Check: Python 3.10, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.11, Django 5.2 (ubuntu-latest)
  • GitHub Check: benchmarks

@joshuadavidthomas joshuadavidthomas changed the title migrate to TagSpecs v0.5.0 spec migrate to TagSpecs v0.6.0 spec Nov 4, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
crates/djls-conf/src/tagspecs/legacy.rs (1)

5-5: Critical: Address rustfmt formatting issue (previously flagged).

As noted in a previous review, line 5 has a formatting issue that does not conform to rustfmt standards. This will cause CI pipeline failures.

Please run the following command to fix all formatting issues:

cargo +nightly fmt
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a710db and 71c7176.

📒 Files selected for processing (8)
  • .mkdocs.yml (1 hunks)
  • CHANGELOG.md (1 hunks)
  • crates/djls-conf/src/lib.rs (9 hunks)
  • crates/djls-conf/src/tagspecs.rs (2 hunks)
  • crates/djls-conf/src/tagspecs/legacy.rs (1 hunks)
  • crates/djls-semantic/src/templatetags/specs.rs (10 hunks)
  • docs/configuration.md (1 hunks)
  • docs/tagspecs.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • docs/configuration.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md
🧰 Additional context used
🧬 Code graph analysis (2)
crates/djls-conf/src/lib.rs (1)
crates/djls-conf/src/tagspecs/legacy.rs (1)
  • convert_legacy_tagspecs (104-131)
crates/djls-conf/src/tagspecs.rs (1)
crates/djls-conf/src/tagspecs/legacy.rs (1)
  • default_true (96-98)
🪛 LanguageTool
docs/tagspecs.md

[style] ~89-~89: Using many exclamation marks might seem excessive (in this case: 6 exclamation marks for a text that’s 4152 characters long)
Context: ...s extra.choices) ## Common Patterns !!! note Examples below use djls.toml...

(EN_EXCESSIVE_EXCLAMATION)

🪛 markdownlint-cli2 (0.18.1)
docs/tagspecs.md

95-95: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


119-119: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


139-139: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


161-161: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


215-215: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


226-226: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


249-249: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


262-262: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


289-289: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)


300-300: Code block style
Expected: indented; Actual: fenced

(MD046, code-block-style)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Python 3.14, Django main (ubuntu-latest)
  • GitHub Check: Python 3.13, Django 5.2 (ubuntu-latest)
  • GitHub Check: Python 3.12, Django 6.0a1 (ubuntu-latest)
  • GitHub Check: Python 3.10, Django 4.2 (ubuntu-latest)
🔇 Additional comments (21)
.mkdocs.yml (1)

23-25: LGTM! Tabbed extension configured appropriately.

The addition of the pymdownx.tabbed extension with alternate_style and combine_header_slug options enables tabbed content in the documentation, which is used effectively in docs/tagspecs.md to show configuration examples in both djls.toml and pyproject.toml formats.

crates/djls-conf/src/lib.rs (6)

16-16: LGTM! Public API appropriately expanded.

The new public exports for ArgKindDef, PositionDef, TagDef, TagLibraryDef, and TagTypeDef appropriately expose the v0.6.0 schema types needed by downstream crates like djls-semantic.

Also applies to: 21-21, 25-25, 27-30


44-52: LGTM! Well-implemented utility function.

The log_dir() function provides a clean API for obtaining the log directory with appropriate fallback behavior and error handling.


128-130: LGTM! Override logic correctly updated.

The override handling correctly checks overrides.tagspecs.libraries.is_empty() to align with the new hierarchical TagSpecDef structure.


204-206: LGTM! Accessor signature correctly updated.

The tagspecs() method now returns &TagSpecDef instead of &[TagSpecDef], correctly reflecting the change from a flat list to a hierarchical document structure.


547-608: LGTM! Tests comprehensively updated.

The tests correctly validate the new hierarchical TagSpecDef structure with library grouping, and the test assertions properly access libraries[0].tags[0] etc.


849-1002: LGTM! Excellent legacy format test coverage.

The new legacy_format test module provides comprehensive validation of the v0.4.0 to v0.6.0 conversion, including:

  • Flat format conversion to hierarchical structure
  • End tag optional/required inversion
  • Choice argument conversion with extra metadata
  • Module-based grouping
crates/djls-semantic/src/templatetags/specs.rs (5)

153-177: LGTM! Library-based tag conversion correctly implemented.

The From<&Settings> implementation properly iterates through tagspec_doc.libraries and converts each tag with its module, then merges user specs to override built-ins. The conversion flow is clean and correct.


187-227: LGTM! Tag type handling and end tag synthesis correct.

The conversion properly handles the three tag types:

  • Block: synthesizes end{tagname} if not provided
  • Loader: uses provided end or None
  • Standalone: enforces no end tag

The default values for synthesized end tags (optional: false, empty args) are appropriate.


340-383: LGTM! Argument kind mappings correctly implemented.

The conversion properly maps the new ArgKindDef variants to runtime TagArg types:

  • Literal/Syntax/Modifier unified as TagArg::Literal (appropriate for token-based args)
  • Choice correctly extracts choices from extra.metadata["choices"]
  • Graceful handling of missing metadata with unwrap_or_default()

392-404: LGTM! Required/optional inversion correctly implemented.

The conversion properly inverts the semantics: required in the config spec becomes optional in the runtime representation. The comment clearly documents this intentional inversion.


757-949: LGTM! Comprehensive conversion test coverage.

The tests thoroughly validate:

  • Individual type conversions (TagArgDef, EndTagDef, IntermediateTagDef)
  • Full TagDef to TagSpec conversion with module
  • Settings integration with library grouping
  • Override behavior (user specs override built-ins)
  • Required/optional semantic inversion
crates/djls-conf/src/tagspecs/legacy.rs (5)

21-99: LGTM! Legacy structures correctly defined.

The legacy data structures accurately represent the v0.4.0 flat format with appropriate serde attributes and defaults. The default_true() helper correctly provides the legacy default for the required field.


105-132: LGTM! Conversion correctly groups tags by module.

The convert_legacy_tagspecs function properly:

  • Groups legacy tags by module using HashMap
  • Converts grouped tags into TagLibraryDef structures
  • Creates a valid v0.6.0 TagSpecDef root document

134-173: LGTM! Conversion helpers handle structure differences correctly.

The helper functions properly:

  • Determine tag type (Block if has end_tag, else Standalone)
  • Invert end tag optional→required semantics
  • Provide appropriate defaults for new v0.6.0 fields (min, max, position)

175-211: LGTM! Argument type mappings correctly handle legacy to v0.6.0 conversion.

The conversion properly:

  • Maps legacy types to new ArgKindDef variants
  • Unifies String/Variable → Variable (reasonable for template context)
  • Unifies Expression/VarArgs → Any (appropriate for flexible arguments)
  • Stores Choice values in extra metadata as required by v0.6.0 spec

217-417: LGTM! Comprehensive test coverage for legacy conversion.

The tests thoroughly validate:

  • Simple and block tag conversion
  • Optional→required semantic inversion
  • All argument type mappings
  • Choice metadata extraction and storage
  • Module-based grouping
  • Intermediate tag conversion
crates/djls-conf/src/tagspecs.rs (4)

32-68: LGTM! Hierarchical structure correctly defined.

The TagLibraryDef and TagDef structures properly implement the v0.6.0 hierarchical format with library-based grouping, appropriate fields, and correct serde attributes.


70-128: LGTM! Type classification and constraint fields well-designed.

The enum definitions (TagTypeDef, PositionDef) and structure expansions (EndTagDef, IntermediateTagDef) properly model the v0.6.0 specification with appropriate constraints (min/max occurrences, positioning) and semantic fields.


130-181: LGTM! Argument modeling comprehensively handles semantic roles.

The TagArgDef structure and associated enums (ArgTypeDef, ArgKindDef) provide clear separation between:

  • Positional/keyword designation (ArgTypeDef)
  • Semantic role (ArgKindDef: Any, Assignment, Choice, Literal, Modifier, Syntax, Variable)

The extra field allows for metadata storage (e.g., choices for Choice kind), and all fields have appropriate defaults.


183-201: LGTM! Default functions provide appropriate values.

The helper functions correctly provide defaults for serde deserialization:

  • default_version(): "0.6.0"
  • default_engine(): "django"
  • default_position(): PositionDef::Any
  • default_arg_type(): ArgTypeDef::Both

These align with the v0.6.0 specification expectations.

@joshuadavidthomas joshuadavidthomas merged commit 2da341b into main Nov 4, 2025
34 checks passed
@joshuadavidthomas joshuadavidthomas deleted the tagspecs-v0.5.0 branch November 4, 2025 16:15
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