| I created a Go language extension that turns HTML templates into typed Go expressions and adds `elem` primitives. Works via own language server by proxying gopls with extra features on top + runtime package. In core ideas it's sumular to `templ` - it compiles to Go, has own extension (`.gox`), language server, CLI tool and IDE plugins, can write to `io.Writer`. But there are much more differences. 1. HTML supported as a first-class Go expression. For example this is valid syntax: `hello := <h1>Hello World!</h1>`. Also `elem` keyword is acting like `templ`, but supports anonymous functions: ```
var f = elem(text string) {
<h1>~(text)</h1>
}
``` 2. I aimed for superior LSP experience, my language server architecture is different, what allowed be to enable LSP edits (rename works!) and support seamless LSP navigation (go to definition, calls, etc - won't point you to generated file). Also it manages generated files automatically (compiles as you type and writes as you save, no need to run `generate` command manually) 3. Improved parsing stability. I based parser on extended Go tree-sitter grammar + made syntax more distinctive. In practice, this means better developer experience during edits and less artifacts (it's not triggered by keywords in a plain text) 4. Different primitives set. I have `gox.Elem` which is a concrete type and `gox.Comp` interface with `Main() gox.Elem` function. That allowed to extend feature set on basic `gox.Elem` while enable you to write components without lowering to `Render(ctx, io.Writer)` interface. 5. Extensible rendering pipeline. In `GoX` HTML is converted to stream of typed jobs, that can be preprocessed in any way. For example you can add your own element `<eb>` and on render convert it to `<span class="font-extrabold">`. Also it has various extensibility points, like you can read and alter attributes as regular go values. There is even special interface `gox.Modify`, that can read and set/unset any attributes when rendered: `<svg (styles.NormSVG) ...>` can set `fill`, `stoke` and unset existing width/height. I basically tried to solve all issues I had with templ while shifting general design towards better extensibility with minimal core and I think I succeed. In terms of raw performance, templ will beat `GoX` every time. Extra layer I added is still an extra layer. It's microsecond scales, can't imagine it as a bottleneck, but anyway. P.S. I build it primarily to support my server-driven web app runtime/framework, but it works standalone perfectly and is templ-compatible. |
My theory that I don't have to care a lot about gopls changes, because I rely on LSP protocol itself when doing conversion. I basically scan exchange JSONs for locations (sometimes recursively) that are related to `.gox` or generated `.x.go` and perform targeted patches.
About string literals completions.. I can't imagine how I could achieve even a 1/10 of feature set with this approach. Or at the end, it will turn out as something very similar to what I have, but achieved from a different starting point.