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}"
+ );
+ }
}
}