content");
+ type(closing, ">");
+ expect(closing.state.doc.toString()).toBe("
contentv>");
+ });
+
+ it("undoes the pair as one step and redoes it", () => {
+ const v = make("
", history());
+ type(v, "x");
+ expect(v.state.doc.toString()).toBe("
");
+ expect(undo(v)).toBe(true);
+ expect(v.state.doc.toString()).toBe("
");
+ expect(redo(v)).toBe(true);
+ expect(v.state.doc.toString()).toBe("
");
+ });
+
+ // Every mode whose outer parser is `@lezer/html`: renaming an opening tag follows into
+ // the closing one (prefs.ts enables the filter for exactly these language ids).
+ describe("in the html-based modes", () => {
+ const modes: [string, () => Extension][] = [
+ ["Vue", () => vueLanguage()],
+ ["Angular Template", () => angular()],
+ ["JSP", () => jsp()],
+ ["Django Template", () => django()],
+ ["Astro", () => astro()],
+ ];
+
+ for (const [name, language] of modes) {
+ it(`renames the closing tag while the opening one is typed in ${name}`, () => {
+ const v = makeWith([language(), linkedTags], '
x
');
+ type(v, "s");
+ expect(v.state.doc.toString()).toBe('
x');
+ });
+ }
+ });
+
+ describe("the smart key gates the filter", () => {
+ /** The real wiring: prefsExtension attaches the filter for the document's language. */
+ const prefs = (language: string | undefined, smartKeys: Record
= {}) =>
+ makeWith(
+ [
+ EditorState.allowMultipleSelections.of(true),
+ // A parseable tree: the real editors attach the mode's parser beside prefsExtension.
+ html(),
+ prefsExtension({
+ prefs: validateEditorExtras({ smartKeys }),
+ path: "",
+ softWrap: false,
+ showWhitespace: false,
+ hardWrapAt: 120,
+ readOnly: false,
+ language,
+ }),
+ ],
+ "",
+ );
+
+ it("links tags for plain html", () => {
+ const v = prefs("html");
+ type(v, "s");
+ expect(v.state.doc.toString()).toBe("");
+ });
+
+ it("links tags for the html-based language ids", () => {
+ for (const language of ["vue", "jsp", "django-html", "astro"]) {
+ const v = prefs(language);
+ type(v, "s");
+ expect(v.state.doc.toString()).toBe("");
+ }
+ });
+
+ it("stays off when the option is off", () => {
+ const v = prefs("html", { simultaneousTagEditing: false });
+ type(v, "s");
+ expect(v.state.doc.toString()).toBe("");
+ });
+
+ it("stays off for the token-based erb mode", () => {
+ const v = prefs("erb");
+ type(v, "s");
+ expect(v.state.doc.toString()).toBe("