Comments (4)
I had a look at implementing the idea in quicktemplate, but the templating language felt a bit too complex to bite off adding the features in one go (things like the spacing operators, support for outputs other than HTML, inclusion of arbitrary files etc.). Also, wasn't sure how the way that the generator emitted code would work, was hoping there would be an object model to parse into, then generate from that.
So I took some things that I liked about quicktemplate, and built something that would be easy to add IDE support, and that targets HTML only. I built a parser that parses to an object model, a Go code generator (compiler) that maintains a sourcemap during code generation, and an LSP that is able to proxy to gopls
over at https://github.com/a-h/templ
The proxy can present autocomplete suggestions so far, but it's not currently possible to use them because I've only rewritten the LSP requests on the way to gopls
, I still need to remap the text ranges on the way back. However, it's showing some promise as an approach:
https://asciinema.org/a/JEiVL1AJOro2OgXDsBtkX0cCb
The syntax highlighting in the demo comes from a vim plugin my friend made.
I think the same approach I've used could work for quicktemplate, by maintaining the source map and using an LSP proxy. What do you think @valyala?
from quicktemplate.
Did you get anywhere with this? One of the things I like about quicktemplate is that you just write Go code, rather than having to have a custom loop syntax etc.
I tried out two plugins for vim and VS Code, but they just provide syntax highlighting. On the Go side of things, I'd like autocomplete, error checking, go to definition, automatic package imports etc., and on the HTML side, tag validation, attribute autocomplete etc.
- https://github.com/b0o/quicktemplate.vim
- https://marketplace.visualstudio.com/items?itemName=vsatomi.vscode-quicktemplate
I think your suggestion of building a Language Server to translate requests back to HTML and Go LSPs (gopls
) would be a good way to support common editors.
Without the editor support, editing isn't as slick as you'd get in something like JSX in JavaScript. For example, this looks right, but will fail at compile time, because the strings
package hasn't been imported.
Hello is a simple template function.
{% func Hello(name string) %}
Hello, {%s strings.ToUpper(name) %}!
{% endfunc %}
Here, I wouldn't get autocomplete on the data
variable, so I'd have to wait until compile time to see that there's no Z
field on the Data
struct.
type Data struct {
A string
B string
}
{% func Hello(data Data) %}
Z: {%s data.Z %}!
{% endfunc %}
from quicktemplate.
Looking into this a bit further, I think the best approach might be to create an LSP for quicktemplate and to add the required features one-by-one.
gopls
doesn't seem to be a good basis, because the internal
parts of the project can't easily be referenced, but https://github.com/sourcegraph/go-langserver looks useful.
While the sourcegraph example is good, it had loads of configuration and so it was a bit complicated: https://github.com/sourcegraph/go-langserver/blob/4b49d01c8a692968252730d45980091dcec7752e/main.go#L173
So, I cut it down to be a minimal example and hooked it up to Neovim with the CoC plugin (https://github.com/neoclide/coc.nvim) for testing. To get Neovim to send commands to my LSP binary, I had to update my CoC config to add in my .qtpl
language server.
{
"languageserver": {
"qtpl": {
"command": "/Users/adrian/github.com/a-h/qt-lsp/qt-lsp",
"filetypes": ["qtpl"]
}
}
Next, I had to tell Neovim that the file I was working on was a qtpl
file with the :set filetype=qtpl
command.
Then, I could read the logs the LSP left behind:
{"level":"info","ts":1617638502.683124,"caller":"qt-lsp/main.go:23","msg":"Starting up..."}
{"level":"info","ts":1617638502.683669,"caller":"qt-lsp/handler.go:26","msg":"request","req":{"method":"initialize","params":{"processId":47279,"rootPath":"/Users/adrian/github.com/a-h","rootUri":"file:///Users/adrian/github.com/a-h","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]}},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]}},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"contextSupport":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"activeParameterSupport":true,"parameterInformation":{"labelOffsetSupport":true}}},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true},"implementation":{"dynamicRegistration":true},"declaration":{"dynamicRegistration":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"selectionRange":{"dynamicRegistration":true}},"window":{"workDoneProgress":true}},"initializationOptions":{},"trace":"off","workspaceFolders":[{"uri":"file:///Users/adrian/github.com/a-h","name":"a-h"}],"clientInfo":{"name":"coc.nvim","version":"0.0.80"},"workDoneToken":"d4b23a3d-6591-477f-92b9-2ca9433ee73b"},"id":0,"jsonrpc":"2.0"}}
{"level":"info","ts":1617638502.683837,"caller":"qt-lsp/handler.go:32","msg":"response","resp":{"capabilities":{"textDocumentSync":2,"hoverProvider":true,"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"referencesProvider":true,"documentSymbolProvider":true,"implementationProvider":true}}}
From here, I think I could work out how to add an autocomplete for functions that are in scope, to suggest imports etc., and to enable snippets.
from quicktemplate.
I got a "Hello World" running in Neovim. It doesn't actually do anything useful, just returns a constant for autocomplete requests.
Here's what that looks like:
https://asciinema.org/a/D0efR6Inmel9JHEZiqHxbCSuR
Here's the demo language server:
from quicktemplate.
Related Issues (20)
- New Feature Request - optimized right/left padding %s option
- Uint32 and Uint64 support? HOT 3
- Function type which is called once at compile time HOT 1
- don't work {% case []string %} inside {% switch r.(type) %} HOT 3
- Is quicktemplate safe to render external template from the user?
- Use custom Localizer/Formatter
- Allow Call Render Function in Objects
- Why Quicktemplate is NOT the faster? HOT 1
- [Question] leading new line HOT 2
- Multiple languages in templates
- HTML Encoding happens even when equal signs are being used to turn off encoding HOT 1
- Go language server ignores the compiled templates
- hot reload template
- Security: templates are vulnerable to XSS
- Can quicktemplate be used for replacing text/template? HOT 1
- Should a template define at least one function?
- qtc binary is not installed by the command suggested by README HOT 1
- Compile to wasm when template changes
- fasthttp inadvertently pulled in as a dependency
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from quicktemplate.