feat(php): built-in PHP live templates (#1372)

PHP files had no built-in live templates. A PHP group now ships with the
others (Settings › Editor › Live Templates, editable and switchable like
them):

- fore / forek: foreach over an iterable, as value or key => value, the
  caret starting on the iterable
- pubf, prof, prif and pubsf, prosf, prisf: public, protected or private
  method, static or not
- thr (throw new), eco (echo), inc / inco / rqr / rqro (include,
  include_once, require, require_once)

PHP variables are written `$$NAME$` in the bodies, so they expand with
their `$`.

Fixes #1234
This commit is contained in:
l0ng-ai
2026-11-05 06:06:39 +08:00
committed by GitHub
parent 9669e58398
commit a7f0f8110b
2 changed files with 52 additions and 0 deletions
@@ -154,6 +154,18 @@ describe("expansion", () => {
for (const a of ["psvm", "sout", "fori", "iter", "main"]) expect(has("java", a)).toBe(true);
for (const a of ["main", "fun", "val", "sout"]) expect(has("kotlin", a)).toBe(true);
for (const a of ["todo", "fixme"]) expect(has("go", a)).toBe(true);
for (const a of ["fore", "forek", "pubf", "prisf", "thr", "eco", "rqro"]) expect(has("php", a)).toBe(true);
expect(has("java", "fore")).toBe(false);
});
it("writes PHP variables with their $", () => {
const php = (abbrev: string) => BUILTIN_TEMPLATES.find((t) => t.id === `php.${abbrev}`)!;
const fore = apply("", expandTemplate(php("fore")).snippet, 0);
expect(fore.text).toBe("foreach ( as $value) {\n \n}");
// The caret starts on the iterable.
expect(fore.sel[0]).toEqual([9, 9]);
expect(apply("", expandTemplate(php("forek")).snippet, 0).text).toBe("foreach ( as $key => $value) {\n \n}");
expect(apply("", expandTemplate(php("prosf")).snippet, 0).text).toBe("protected static function ()\n{\n \n}");
});
it("merges user edits, deletions and user templates", () => {
+40
View File
@@ -264,6 +264,45 @@ const kotlin = group(
],
);
const method = (abbrev: string, modifiers: string): Def => ({
abbrev,
description: `${modifiers} function`,
body: `${modifiers} function $NAME$($PARAMETERS$)\n{\n\t$END$\n}`,
});
/** PHP's `$` is written `$$` in a template body. */
const php = group(
"PHP",
"php",
["php"],
[
{
abbrev: "fore",
description: "foreach (iterable as value)",
body: "foreach ($ITERABLE$ as $$$VALUE$) {\n\t$END$\n}",
variables: [v("ITERABLE"), v("VALUE", undefined, '"value"')],
},
{
abbrev: "forek",
description: "foreach (iterable as key => value)",
body: "foreach ($ITERABLE$ as $$$KEY$ => $$$VALUE$) {\n\t$END$\n}",
variables: [v("ITERABLE"), v("KEY", undefined, '"key"'), v("VALUE", undefined, '"value"')],
},
method("pubf", "public"),
method("prof", "protected"),
method("prif", "private"),
method("pubsf", "public static"),
method("prosf", "protected static"),
method("prisf", "private static"),
{ abbrev: "thr", description: "throw new", body: "throw new $END$;" },
{ abbrev: "eco", description: "echo", body: 'echo "$END$";' },
{ abbrev: "inc", description: "include", body: "include '$END$';" },
{ abbrev: "inco", description: "include_once", body: "include_once '$END$';" },
{ abbrev: "rqr", description: "require", body: "require '$END$';" },
{ abbrev: "rqro", description: "require_once", body: "require_once '$END$';" },
],
);
const general = group(
"General",
"any",
@@ -458,6 +497,7 @@ export const BUILTIN_TEMPLATES: readonly LiveTemplate[] = [
...go,
...java,
...kotlin,
...php,
...sqlQuery,
...sqlStatement,
...sqlCode,