diff --git a/.version b/.version index 0cbceb828..e84eeddd7 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.2.742 \ No newline at end of file +0.2.745 \ No newline at end of file diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go index e6f24b0ae..8ac3597b2 100644 --- a/cmd/templ/lspcmd/proxy/server.go +++ b/cmd/templ/lspcmd/proxy/server.go @@ -396,10 +396,12 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( // Ensure that Go source is available. gosrc := strings.Split(p.GoSource[string(templURI)], "\n") - if len(gosrc)-1 < int(params.TextDocumentPositionParams.Position.Line) { + if len(gosrc) < int(params.TextDocumentPositionParams.Position.Line) { + p.Log.Info("completion: line position out of range") return nil, nil } - if len(gosrc[params.TextDocumentPositionParams.Position.Line])-1 < int(params.TextDocumentPositionParams.Position.Character) { + if len(gosrc[params.TextDocumentPositionParams.Position.Line]) < int(params.TextDocumentPositionParams.Position.Character) { + p.Log.Info("completion: col position out of range") return nil, nil } diff --git a/docs/docs/05-server-side-rendering/05-streaming.md b/docs/docs/05-server-side-rendering/05-streaming.md new file mode 100644 index 000000000..e6a84480d --- /dev/null +++ b/docs/docs/05-server-side-rendering/05-streaming.md @@ -0,0 +1,42 @@ +# HTTP Streaming + +The default behaviour of the `templ.Handler` is to render the template to a buffer and then write the buffer to the response. + +This ensures that the template has successfully rendered before the response is sent to the client, so that appropriate repsonse codes can be set if the template fails to render, and partial responses are not sent to the client. + +## Rendering lifecycle + +Typical usage of templ involves collecting data that is used to populate the template, before rendering the template and sending a response. + +For example, executing several database queries, calling an API, or reading from a file, before rendering the template. + +```mermaid +flowchart TD; + r[Request] + q[DB Queries] + q1[Query result] + q2[Query result] + a[API Calls] + api[API call result] + t[Render template] + h[HTML] + response[Response] + r-->q; + r-->a; + q-->q1 + q-->q2 + a-->api + q1-->t + q2-->t + api-->t + t-->h + h-->response; +``` + +However, if the queries and API calls take a long time, this has an impact on Time to First Byte (TTFB) because the client has to wait for all database queries and API calls to complete before sending the response. + +To improve TTFB, the template can be streamed to the client as soon as the first part of the template is rendered, while the remaining queries and API calls are still in progress, at the cost of not being able to set response codes or headers after the first part of the template is rendered. + +## Enabling streaming + +Streaming can be enabled by setting the `Streaming` field of the `templ.Handler` to `true`. diff --git a/examples/counter/main.go b/examples/counter/main.go index 455df25b4..f8140a459 100644 --- a/examples/counter/main.go +++ b/examples/counter/main.go @@ -14,7 +14,7 @@ import ( ) func main() { - log := slog.New(slog.NewJSONHandler(os.Stderr)) + log := slog.New(slog.NewJSONHandler(os.Stderr, nil)) s, err := db.NewCountStore(os.Getenv("TABLE_NAME"), os.Getenv("AWS_REGION")) if err != nil { log.Error("failed to create store", slog.Any("error", err))