Giter Club home page Giter Club logo

Comments (4)

a-h avatar a-h commented on June 2, 2024 7

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.

a-h avatar a-h commented on June 2, 2024

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.

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.

a-h avatar a-h commented on June 2, 2024

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.

a-h avatar a-h commented on June 2, 2024

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:

https://github.com/a-h/qt-lsp

from quicktemplate.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.