Keeping the Template Valid for Your IDE
Your IDE reads a template before CodeBaker does, so it sees the tags too. In a PHP, Java or TypeScript file a {% if %} in the middle of a class is a syntax error, and the template stops being a file you can work in. This lesson shows how to keep the conditions and loops and still have a valid file: put each tag in a comment.
There are two tools, and you use them together:
- Values are written as a declared placeholder such as
___name___, which is a valid identifier in most languages (Values and Variables). - Logic (conditions and loops) goes in a comment that starts with
CodeBaker:, which the IDE sees as an ordinary comment.
A tag in a comment
Write the tag after CodeBaker: in a comment. CodeBaker removes the comment, and what follows CodeBaker: takes its place:
//CodeBaker: {% if hasTimestamps %} → {% if hasTimestamps %}
Here is a PHP model that uses a placeholder for its class name, a loop for its properties and a condition for one more. Every tag is in a comment, and the template is valid PHP:
https://codebaker.dev/examples/product-model.zipNeeds the CodeBaker plugin and an open project. Another IDE, or the button does nothing? Copy the link, then in the IDE right-click any folder → New → Generate from Template → Install Template from URL… and paste it. You always get a confirmation dialog before anything is installed.
<?php
class ___modelName___
{
//CodeBaker: {% for field in fields %}
public ___field_dot_type___ $___field_dot_name___;
//CodeBaker: {% endfor %}
//CodeBaker: {% if withTimestamps %}
public DateTimeImmutable $createdAt;
//CodeBaker: {% endif %}
}
{
// The folder name when the template is installed under .CodeBaker/
id: "product-model",
name: "PHP Model",
// The placeholder style used in the template files: ___modelName___, ___field_dot_name___ ...
placeholders: ["___VAR___"],
// Each variable becomes a text field in the dialog, with this as its default value.
vars: {
modelName: "Product",
withTimestamps: "true",
},
// Lists go in the json area of the dialog. Each top-level key is a variable too.
json: {
fields: [
{ name: "title", type: "string" },
{ name: "price", type: "float" },
],
},
// Opened in the editor after generation (the path is relative to the folder you generated into).
open: ["___modelName___.php"],
}
- modelName
- Product
- withTimestamps
- true
{
"fields": [
{
"name": "title",
"type": "string"
},
{
"name": "price",
"type": "float"
}
]
}
<?php
class Product
{
public string $title;
public float $price;
public DateTimeImmutable $createdAt;
}
Look at the result: the lines that held only a directive left no empty line, the loop wrote one property per item, and the file name ___modelName___.php became Product.php.
The rules
-
Comment styles:
//,/* */,#,--and<!-- -->. A closing*/or-->is optional and is removed too.Prefix Languages //PHP, Java, Kotlin, JavaScript, TypeScript, Go, SCSS, LESS /* ... */CSS and C-style block comments: write /* CodeBaker: ... */#Python, Ruby, shell, YAML --SQL, Lua <!-- ... -->HTML, XML, Markdown //is not a comment in plain CSS or in a<style>block, so write/* CodeBaker: ... */there. -
The directive is the last thing on its line. It runs to the end of the line, so anything after it belongs to it.
-
Only
CodeBaker:makes a directive. The word and the colon are both required, and the word is case-sensitive.// CodeBaker is a tool,// see CodeBaker: docsand// codebaker: xare ordinary comments. -
The tag line leaves no empty line. A line that holds only tags joins the next line, which keeps its own indentation.
-
Indentation of the comment is not kept. Text that replaces a full-line comment starts at the beginning of the line. After code on the same line, the text is appended to that code:
echo 1; //CodeBaker: {% if x %}becomesecho 1; {% if x %}. -
Your IDE shows which comments are directives. The CodeBaker plugin colors the
CodeBaker:that starts one (teal on a light theme, cyan on a dark one), and does not color a comment that only mentions CodeBaker.
A few words after CodeBaker: are commands, not text, and nothing is written for them:
replace,regex,off,onandresetdefine replacement rules: Replacing Real Values.separatorbecomes a comma between the items of a loop: Generating JSON.{% slot name %},{% endslot %},{% fragment id %}and{% endfragment %}mark a place for theinserttype, bare or in a comment. They are copied into the generated file exactly as written: Advanced Templates.
The same name twice
Because the IDE reads both branches of a condition, it sees the second declaration as a duplicate:
class User extends Model{ //CodeBaker: {% if hasTimestamps %} public $timestamps = true; //CodeBaker: {% else %} public $timestamps = false; //CodeBaker: {% endif %}}The generated file has only one of them, so the error exists only in the template. To silence it, add a placeholder that exists only to make the name unique, a throwaway suffix. It disappears when the file is generated:
class Config
{
//CodeBaker: {% if cacheDriver == 'redis' %}
protected $driver = 'redis';
//CodeBaker: {% elseif cacheDriver == 'file' %}
protected $driver___alt1___ = 'file';
//CodeBaker: {% else %}
protected $driver___alt2___ = 'array';
//CodeBaker: {% endif %}
}
With cacheDriver = file, the output is protected $driver = 'file';.
How it works. ___alt1___ is converted to {{ alt1 }} before Pebble runs (the template declares the ___VAR___ style). Pebble keeps only the branch whose condition holds. alt1 is not defined, and Pebble prints an undefined variable as an empty string, so the suffix vanishes.
Rules for a suffix
- It must not be a real variable.
$name___modelName___becomes$nameUserifmodelNameis defined, invars, in the dialog or as a loop variable. Names such asalt1,alt2,disabledandsyncare safe. - It must not be a Pebble keyword or literal.
___true___printstrueand___false___printsfalse.___and___,___or___,___not___and___is___fail with a Pebble parse error. - The IDE sees two different names, so rename and find usages do not connect them. If only a method body differs, put the condition inside the body and skip the suffix.
The same idea resolves a duplicate key in a .json5 template: see Generating JSON.
What the IDE cannot know
- A loop variable such as
itemorfieldin a//CodeBaker: {% for %}loop is unknown to the IDE, so it may be reported as undefined in the template. - A value in a position that accepts no identifier, such as a CSS value, still needs another tool: Replacing Real Values.
These affect only the template: the generated code contains none of the directive comments. Known Limitations lists every case with what to do.
Next
- Names and Filters: compute class, table and route names from one value.
- Replacing Real Values: keep a real value in the file.
- Known Limitations: where a template can still trip up the IDE.