diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 39c63dcb6..7d25484f0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Named Blade slot closers are recognized.** A slot written as `` no longer reports a mismatched component tag. Contributed by @shuvroroy. - **`phpantom_lsp fix` runs its workers on the same stack as `analyze`.** The parse and fix workers were spawned with the 2 MB default a thread gets, where `analyze` gives its workers the 8 MB the recursive parser and type walker need, so a project holding a deeply nested file could crash `fix` outright where `analyze` completed. Both commands now share one parse phase, and `fix` also runs the Laravel discovery `analyze` does before fixing, so the two see the same project. - **A formatting edit measures the last line in UTF-16 units.** The whole-document replacement a formatter produces ended at a column counted in bytes, so a file whose unterminated last line held multibyte text was sent an end position past that line. - **Pint reads the project's `pint.json`.** Pint looks for its configuration in the directory it is started from, and it was started in the language server's own directory, so an editor that launches the server from a subdirectory or a multi-root workspace had Blade and PHP files formatted with Pint's default `laravel` preset rather than the project's. Pint, php-cs-fixer, and phpcbf now run with the workspace root as their working directory. diff --git a/src/blade/component_tags.rs b/src/blade/component_tags.rs index 8564604a3..4fc603ca5 100644 --- a/src/blade/component_tags.rs +++ b/src/blade/component_tags.rs @@ -494,16 +494,12 @@ pub(crate) fn tag_spans(content: &str) -> Vec { /// Whether a closing tag spelled `closer_name` ends an opener of kind /// `kind` named `name`. /// -/// Every tag closes under its own name, with one exception Blade's own -/// compiler carves out: a named slot (``, or the legacy -/// ``) still closes with the bare ``, never -/// repeating the slot's own name in the closing tag. +/// Every tag closes under its own name. A named slot (``) +/// also accepts the bare `` used by the legacy +/// `` form. fn closer_matches(kind: TagKind, name: &str, closer_name: &str) -> bool { - if kind == TagKind::Blade && is_slot_tag_name(name) { - closer_name == "slot" - } else { - name == closer_name - } + name == closer_name + || (kind == TagKind::Blade && is_slot_tag_name(name) && closer_name == "slot") } /// A place a component tag's block structure does not add up: the same @@ -1468,15 +1464,25 @@ mod tests { assert_eq!(names, ["card", "alert", "note"]); } - /// A named inline slot closes with the bare ``, not - /// ``; `tag_spans` has to know that too or every named - /// slot in the file comes back unclosed. + /// A named inline slot may close with the bare ``. #[test] fn a_named_slot_closes_with_the_bare_tag() { let blade = "\n\nHi\n\n\n"; assert_eq!(tag_bodies(blade).len(), 2); } + #[test] + fn a_named_slot_closes_with_its_own_name() { + let blade = "\n\nHi\n\n\n"; + let tags = tag_spans(blade); + assert_eq!(tags.len(), 2); + assert!(tags.iter().all(|tag| tag.closed)); + assert_eq!( + &blade[tags[1].span.clone()], + "\nHi\n" + ); + } + /// The imbalances [`tag_imbalances`] finds, as short strings for /// readable assertions: `"mismatched /"`, /// `"unexpected "`, `"unclosed "`. @@ -1550,10 +1556,24 @@ mod tests { ); } - /// A named inline slot that is properly closed reports nothing, even - /// though its closing tag never repeats the slot's own name. + /// Both closing spellings of a named inline slot are balanced. #[test] fn a_properly_closed_named_slot_reports_nothing() { - assert!(tag_report("\n\nHi\n\n\n").is_empty()); + for closer in ["slot", "slot:title"] { + let blade = format!("\n\nHi\n\n\n"); + assert!(tag_report(&blade).is_empty(), "{blade}"); + } + } + + #[test] + fn a_named_slot_closed_by_a_different_tag_is_mismatched() { + for closer in ["slot:other", "alert"] { + let blade = format!("\n\nHi\n\n\n"); + assert_eq!( + tag_report(&blade), + [format!("mismatched /")], + "{blade}" + ); + } } }