Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/cm/supportedModes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,35 @@
() => [],
);

// Languages that @codemirror/language-data only covers with legacy stream
// tokenizers and that the official Additional Language Modes plugin
// (acode.additional.langmodes) provides as Lezer grammars. They are left out
// so the plugin registers them under their canonical names; without it, these
// files fall back to plain text and Acode recommends the plugin.
const PLUGIN_PROVIDED_LANGUAGES = new Set([
"clojure",
"clojurescript",
"edn",

Check warning on line 163 in src/cm/supportedModes.ts

View workflow job for this annotation

GitHub Actions / Check spelling

"edn" should be "end".
"gas",
"kotlin",
"latex",
"stex",
"lua",
"swift",
]);

// Extensions other modes claim in @codemirror/language-data but that the
// plugin has dedicated modes for: HTML lists Handlebars templates.
const PLUGIN_PROVIDED_EXTENSIONS = new Set(["hbs", "handlebars"]);

// 2) Register all languages provided by @codemirror/language-data
// We convert extensions like [".js", ".mjs"] into a modelist pattern: "js|mjs"
// and preserve aliases and filename regexes for languages like C++ and Dockerfile.
for (const lang of languages as readonly LanguageDescription[]) {
try {
const name = String(lang?.name || "").trim();
if (!name) continue;
if (PLUGIN_PROVIDED_LANGUAGES.has(normalizeModeKey(name))) continue;

const aliases = collectAliases(name, lang.alias);
const modeId = getModeId(name, aliases);
Expand Down Expand Up @@ -248,7 +270,9 @@
for (const e of langExtensions) {
if (typeof e !== "string") continue;
const cleaned = e.replace(/^\./, "").trim();
if (cleaned) parts.push(cleaned);
if (cleaned && !PLUGIN_PROVIDED_EXTENSIONS.has(cleaned.toLowerCase())) {
parts.push(cleaned);
}
}

const filenameMatchers: RegExp[] = [];
Expand Down
104 changes: 104 additions & 0 deletions tests/unit/pluginProvidedModes.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { afterEach, describe, expect, it } from "vitest";
import "cm/supportedModes";
import { addMode, getMode, getModeForPath, removeMode } from "cm/modelist";

// Modes the Additional Language Modes plugin (acode.additional.langmodes)
// replaces with Lezer grammars, with the extensions it registers.
const pluginModes = [
{ name: "assembly", extensions: ["asm", "s", "S", "nasm"] },
{ name: "c", extensions: ["c", "h", "ino"] },
{ name: "clojure", extensions: ["clj", "cljs", "cljc", "cljx", "edn"] },

Check warning on line 10 in tests/unit/pluginProvidedModes.test.js

View workflow job for this annotation

GitHub Actions / Check spelling

"edn" should be "end".
{ name: "kotlin", extensions: ["kt", "kts"] },
{ name: "latex", extensions: ["tex", "ltx", "sty", "cls", "latex"] },
{ name: "lua", extensions: ["lua"] },
{ name: "swift", extensions: ["swift"] },
{ name: "handlebars", extensions: ["hbs", "handlebars"] },
{ name: "mustache", extensions: ["mustache"] },
];

/** The plugin's registration rule: keep Acode's mode, add "<name>-enhanced". */
function registerLikePlugin({ name, extensions }) {
const registered = getMode(name) ? `${name}-enhanced` : name;
addMode(registered, extensions, registered, () => []);
return registered;
}

describe("legacy modes replaced by the Additional Language Modes plugin", () => {
const registered = [];

afterEach(() => {
for (const name of registered.splice(0)) removeMode(name);
});

it.each([
"clojure",
"clojurescript",
"edn",

Check warning on line 36 in tests/unit/pluginProvidedModes.test.js

View workflow job for this annotation

GitHub Actions / Check spelling

"edn" should be "end".
"gas",
"kotlin",
"latex",
"tex",
"stex",
"lua",
"swift",
])("does not ship a built-in %s mode", (name) => {
expect(getMode(name)).toBeNull();
});

it.each([
"a.kt",
"a.kts",
"a.lua",
"a.swift",
"a.clj",
"a.cljs",
"a.edn",

Check warning on line 55 in tests/unit/pluginProvidedModes.test.js

View workflow job for this annotation

GitHub Actions / Check spelling

"edn" should be "end".
"a.s",
"a.tex",
"a.hbs",
"a.handlebars",
])("falls back to plain text for %s without the plugin", (path) => {
expect(getModeForPath(path).name).toBe("text");
});

it("keeps C, C++, HTML and Luau built in", () => {
expect(getModeForPath("a.html").name).toBe("html");
expect(getModeForPath("a.c").name).toBe("c");
expect(getModeForPath("a.cpp").name).toBe("cpp");
expect(getModeForPath("a.luau").name).toBe("luau");
});

it("opens .text files as plain text instead of LaTeX", () => {
expect(getModeForPath("notes.text").name).toBe("text");
});

it("lets the plugin register the replaced modes under canonical names", () => {
for (const mode of pluginModes) registered.push(registerLikePlugin(mode));

expect(registered).toEqual([
"assembly",
"c-enhanced",
"clojure",
"kotlin",
"latex",
"lua",
"swift",
"handlebars",
"mustache",
]);
expect(getModeForPath("a.kt").name).toBe("kotlin");
expect(getModeForPath("a.lua").name).toBe("lua");
expect(getModeForPath("a.swift").name).toBe("swift");
expect(getModeForPath("a.cljs").name).toBe("clojure");
expect(getModeForPath("a.edn").name).toBe("clojure");

Check warning on line 93 in tests/unit/pluginProvidedModes.test.js

View workflow job for this annotation

GitHub Actions / Check spelling

"edn" should be "end".
expect(getModeForPath("a.s").name).toBe("assembly");
expect(getModeForPath("a.tex").name).toBe("latex");
expect(getModeForPath("a.c").name).toBe("c-enhanced");
expect(getModeForPath("a.hbs").name).toBe("handlebars");
expect(getModeForPath("a.handlebars").name).toBe("handlebars");
expect(getModeForPath("a.mustache").name).toBe("mustache");
expect(getModeForPath("a.html").name).toBe("html");
// Luau keeps its own mode even though "lua" now comes from the plugin.
expect(getModeForPath("a.luau").name).toBe("luau");
});
});
Loading