-
-
Notifications
You must be signed in to change notification settings - Fork 297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: change to templ to use single braces #46
Comments
I think this is definitely worth doing, whatever we can to make it easier to use. Shouldn't be massive to fix the vim plugin. Had a thought, do the starting blocks and end blocks need brackets at all? package main
templ name(n string)
{ if n == "" }
<div>Hello, world</div>
{ else }
<div>Hello, {n}</div>
{ endif }
endtempl |
Ha, good point! I need to think about that. If we go far enough down the track, then it stops being HTML with Go code in it, and becomes Go code with HTML in it. Although I could take some shortcuts - basically, just copy everything outside But then,
But if you remove the endtempl, the
And then... templ would basically have to implement the whole of the Go parser, plus some more, which is quite a bit more work. I've got an idea of how that could be simplified, but I haven't thought it through yet. What do you think of this... Outside of templ blocks, everything is Go code, and it's copied as-is to the output. The LSP would work fine. As soon as the parser hits a For each line, the parser has to count the braces (so that we can find the end of the block) and parse strings to avoid mismatches, but each line is just assumed to be Go code and is copied to the output except when the line starts with something templ-y. So, if the line starts with optional whitespace followed by So you'd write something like this.
|
I like that, I especially like that this would make putting the input struct for a template in the same file. Keeping things together. I think writing HTML inside Go is good, currently it's Go inside HTML inside templ. And we want to make it as familiar as possible which means reducing the amount of templ involved, which I think this gives us. |
Started working on it over at https://github.com/a-h/templ/tree/templ_braces |
Got to work out a nice scheme for conditionals within elements. Since Go is the default in this scheme, it's not possible to have bare text, because if someone started a sentence with a Go keyword (e.g. So this would be allowed.
Or
Where t (or This allows for conditionals in text.
|
I rember this library: Some time ago I searched jsx like syntax for templating in go. The great strength of next.js (serverside react) is the typed templating possibilities (jsx). The |
In the branch, I've got an implementation in place, with all the tests passing with the new proposed syntax, code generator working etc.
package main
import "fmt"
import "time"
templ headerTemplate(name string) {
<header data-testid="headerTemplate">
<h1>{ name }</h1>
</header>
}
templ footerTemplate() {
<footer data-testid="footerTemplate">
<div>© { fmt.Sprintf("%d", time.Now().Year()) }</div>
</footer>
}
templ navTemplate() {
<nav data-testid="navTemplate">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/posts">Posts</a></li>
</ul>
</nav>
}
templ layout(name string, content templ.Component) {
<html>
<head><title>{ name }</title></head>
<body>
{! headerTemplate(name) }
{! navTemplate() }
<main>
{! content }
</main>
</body>
{! footerTemplate() }
</html>
}
templ homeTemplate() {
<div data-testid="homeTemplate">Welcome to my website.</div>
}
templ postsTemplate(posts []Post) {
<div data-testid="postsTemplate">
for _, p := range posts {
<div data-testid="postsTemplatePost">
<div data-testid="postsTemplatePostName">{ p.Name }</div>
<div data-testid="postsTemplatePostAuthor">{ p.Author }</div>
</div>
}
</div>
}
templ home() {
{! layout("Home", homeTemplate()) }
}
templ posts(posts []Post) {
{! layout("Posts", postsTemplate(posts)) }
} You'll notice I didn't need to have a I might need to add an empty element as a get-out clause in case someone wants to start a sentence with To migrate from existing templates, it would be possible to have something parse the old templates into the object model using the old parser, drop out some JSON, then generate new templates from that. What do you think of it compared to the existing syntax? |
I think it definitely feels more familiar and intuitive. I think escaping Go code could be solved by supporting multiline strings in some way, for example: A component that displays how to do an if statement in templ: templ ifStatement() {
<div data-testid="ifStatement">
{`if true {
<p data-testid="text">
some text
</p>
}`}
</div>
} This then got me thinking about what if I wanted to display an example of how to use this. templ escapeStatement() {
<div data-testid="escapeStatement">
{`
<div data-testid="ifStatement">
` + "{`" + `if true {
<p data-testid="text">
some text
</p>
}` + "`}" + `
</div>
`}
</div>
} |
I've made some progress on this at https://github.com/a-h/templ/tree/templ_braces The most important change is that I've added a migration command to migrate from v1 to v2 template files. Run
Still to complete is the addition of the new templ v2 enables code like this, assuming it's called
Maybe a bit more thought required about how whitespace is handled, but it's definitely looking right. |
I dealt with the whitespace problems over the weekend, and I tested a migration on a large project. I think it's looking good for a release. |
Looks perfect, much easier to read I think. |
Today I updated the VS Code extension to support v2 syntax. To try it out, you can open up https://github.com/a-h/templ-vscode in VS Code, and hit F5. That will cause a new VS Code window to open, configured with the extension. You'll need to have the |
I've basically rewritten the Language Server, replacing it with https://github.com/go-language-server/protocol because the Sourcegraph implementation was missing anything relating to V3. While I was doing that, I added a new feature to the generate system that outputs a HTML page to view the mapping between Go code within templ files, and the Go code in the generated code. This allows visualisation of the source maps which power the language server. I think the LSP is much easier to work with now, and it's obvious where to implement functionality (server.go). I think it's feature complete now, I'm going to do some more testing, then put out a v2.0 tagged release. |
* feat: start reworking the parser to support bare Go code * wip: continuing parser work * wip: add the if expression back in to support the template parser * wip: fixed broken template file test * wip: refactor from functions to static variables * wip: added support for if statements * wip: add comments to output to show what was parsed * wip: reimplement switch statements * wip: add the if unit tests back in * wip: update README * wip: add for loop support back in * feat: don't render scripts if they are not required * refactor: fix the formatting tests * feat: add support for templates to be receivers, see #46 and #35 (item 1) * refactor: have v1 and v2 side-by-side to enable easier migration * refactor: continue v1 and v2 side-by-side * feat: throw an error if a legacy format is encountered * wip: added start of updated migration function * fix: manually copy between v1 and v2 instead of using copier * feat: complete migration * docs: update for version 2 * fix: broken package test. * fix: broken reformatting of case statements. * feat: continue case statement work * fix: remove additional whitespace * fix: break on newline to allow inline if * fix: bug in Windows line end parsing affecting CSS * feat: add a -w parameter to choose how many cores to use during templ generation * fix: ensure that the server always closes * fix: ensure that code can be at the end of files too * refactor: simplify parsing behaviour to be line oriented * feat: normalise whitespace * feat: use the number of CPUs available to the machine * feat: handle trailing whitespace after case statements * feat: improve the formatting behaviour * feat: set tables to format block style * feat: add style and script elements that ignore the contents * feat: add textDocument/codeLens support * refactor: remove TODO * refactor: copy code from github.com/sourcegraph/go-lsp * refactor: migrate to go.lsp.dev/protocol * feat: migrate the LSP server and rewrite functionality to go.lsp.dev * feat: add sourcemap visualisation * feat: improve sourcemap rendering * feat: fix the source location mapping * refactor: use zero-indexed line indices to match the LSP specification * security: CVE-2022-28948 * chore: upgrade goquery to 1.8.0 * chore: move example to _example to get Go tools to ignore from the top level * feat: rewrite the hover output positions * fix: handle that hover can return nil * feat(lsp): add Implementation * feat: improve LSP capabilities * feat: add declaration and definition support * feat: add DocumentLink, DocumentColor * feat: update LSP edits, replace SourceGraph code to deprecate sourceLength * refactor: use an array of lines to store content, instead of storing the string, to make updates less expensive * fix: update snippets for new syntax * docs: add updated GIF * fix: DidChange storage of updates
In #35, there's a suggestion that the templ expression start and end token of
{%
and%}
is unwieldy.Why did we end up with
{%
/%}
?Initially, when choosing delimiters I was looking for something that was easy to type, reasonable looking, and not likely to show up in code.
I considered double braces
{{
but since the standard library, mustache and hugo all use these, and that double braces show up in Go code, I didn't think it would ideal.I ended up with the
{%
idea because it isn't valid Go code.What would be better?
I think a single brace
{
would be best, because it's the smallest amount of typing, but thought that the complexity of the parser would be high, since I'd have to parse Go code to find the end of statements.How?
However, I think I've got a solution to remove the need for
{%
and to drop it to{
.The templ expressions only show up at particular points in the document (e.g. attribute values, within the main document), there's a strong expectation for the expression to be present, meaning that it's less important for the templ start/end token to be globally unique.
If I use a brace instead, the parser can parse until the closing brace. Braces in Go code must be balanced, so any Go code will balance the brace count, however, the parser would need to ignore braces that are within string literals, since they may contain unbalanced braces.
The basic algorithm is:
{ templ
and setbraceCount
= 1"
handle any escaped quotes or ticks until the end of the string.{
or}
{
dobraceCount++
}
dobraceCount--
braceCount
== 0, breakEOL
, breakEOF
, breakbraceCount
!= 0 throw an errorI've written an implementation here which shows how expressions can be parsed.
https://gist.github.com/a-h/c7c2a04afd5605c48496e5b346956455
Difference between current and new code
Old:
{% if findOut(func() bool { return true }) %}
New:
{ if findOut(func() bool { return true }) }
Old:
{% endif %}
New:
{ endif }
Since the only allowed expression here is a string expression, there's no need to specify the equals sign
{%=
, making it possible to simplify down by two chars.<a href={%= "https://google.com" %} />
<a href={ "https://google.com" } />
The same applies here.
<div>{%= theData() %}</div>
<div>{ theData() }</div>
However, loading a template would still need a specifier:
<div>{%! header() %}</div>
<div>{! header() }</div>
Backwards compatibility
I don't think it's a good idea to break existing code, so I would:
templ migrate
function which does find and replace in templ files, replacing:{%
with{
{%=
with{
{%!
with{!
%}
with}
templ generate
andtempl fmt
- if they see the old pattern{%
in the code, they would stop and print out a suggestion to runtempl migrate
, explain the reasoning for the change, and point to this issue.Ecosystem
The templ vim plugin and VS Code plugin would need to be updated. I don't propose maintaining backwards compatibility for these, since the migration will be one way.
Thoughts / feedback?
Is this worth doing?
Any concerns on this migration?
@joerdav - I assume it's not a big job to migrate the vim plugin if I went ahead with this?
The text was updated successfully, but these errors were encountered: