From a7f0f8110b0b75cbebedfbab79484b9bdaf1277d Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Thu, 5 Nov 2026 06:06:39 +0800 Subject: [PATCH] feat(php): built-in PHP live templates (#1372) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/features/templates/__tests__/live.test.ts | 12 ++++++ src/features/templates/live/builtins.ts | 40 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/features/templates/__tests__/live.test.ts b/src/features/templates/__tests__/live.test.ts index 1b395015..2993f5b7 100644 --- a/src/features/templates/__tests__/live.test.ts +++ b/src/features/templates/__tests__/live.test.ts @@ -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", () => { diff --git a/src/features/templates/live/builtins.ts b/src/features/templates/live/builtins.ts index 59868e79..e8c40ea7 100644 --- a/src/features/templates/live/builtins.ts +++ b/src/features/templates/live/builtins.ts @@ -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,