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,