Skip to content

Commit fb6a5c2

Browse files
authoredSep 23, 2024··
feat(lsp): add workspace folder initialization (#912)
1 parent ff29e12 commit fb6a5c2

File tree

4 files changed

+134
-23
lines changed

4 files changed

+134
-23
lines changed
 

‎cmd/templ/lspcmd/lsp_test.go

+44-19
Original file line numberDiff line numberDiff line change
@@ -335,35 +335,19 @@ func TestReferences(t *testing.T) {
335335
defer teardown(t)
336336
defer cancel()
337337

338-
templFile, err := os.ReadFile(appDir + "/templates.templ")
339-
if err != nil {
340-
t.Fatalf("failed to read file %q: %v", appDir+"/templates.templ", err)
341-
return
342-
343-
}
344-
err = server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
345-
TextDocument: protocol.TextDocumentItem{
346-
URI: uri.URI("file://" + appDir + "/templates.templ"),
347-
LanguageID: "templ",
348-
Version: 1,
349-
Text: string(templFile),
350-
},
351-
})
352-
if err != nil {
353-
t.Errorf("failed to register open file: %v", err)
354-
return
355-
}
356338
log.Info("Calling References")
357339

358340
tests := []struct {
359341
line int
360342
character int
343+
filename string
361344
assert func(t *testing.T, l []protocol.Location) (msg string, ok bool)
362345
}{
363346
{
364347
// this is the definition of the templ function in the templates.templ file.
365348
line: 5,
366349
character: 9,
350+
filename: "/templates.templ",
367351
assert: func(t *testing.T, actual []protocol.Location) (msg string, ok bool) {
368352
expectedReference := []protocol.Location{
369353
{
@@ -391,6 +375,7 @@ func TestReferences(t *testing.T) {
391375
// this is the definition of the struct in the templates.templ file.
392376
line: 21,
393377
character: 9,
378+
filename: "/templates.templ",
394379
assert: func(t *testing.T, actual []protocol.Location) (msg string, ok bool) {
395380
expectedReference := []protocol.Location{
396381
{
@@ -414,6 +399,46 @@ func TestReferences(t *testing.T) {
414399
return "", true
415400
},
416401
},
402+
{
403+
// this test is for inclusions from a remote file that has not been explicitly called with didOpen
404+
line: 3,
405+
character: 9,
406+
filename: "/remoteChild.templ",
407+
assert: func(t *testing.T, actual []protocol.Location) (msg string, ok bool) {
408+
expectedReference := []protocol.Location{
409+
{
410+
URI: uri.URI("file://" + appDir + "/remoteParent.templ"),
411+
Range: protocol.Range{
412+
Start: protocol.Position{
413+
Line: uint32(3),
414+
Character: uint32(2),
415+
},
416+
End: protocol.Position{
417+
Line: uint32(3),
418+
Character: uint32(8),
419+
},
420+
},
421+
},
422+
{
423+
URI: uri.URI("file://" + appDir + "/remoteParent.templ"),
424+
Range: protocol.Range{
425+
Start: protocol.Position{
426+
Line: uint32(7),
427+
Character: uint32(2),
428+
},
429+
End: protocol.Position{
430+
Line: uint32(7),
431+
Character: uint32(8),
432+
},
433+
},
434+
},
435+
}
436+
if diff := lspdiff.References(expectedReference, actual); diff != "" {
437+
return fmt.Sprintf("Expected: %+v\nActual: %+v", expectedReference, actual), false
438+
}
439+
return "", true
440+
},
441+
},
417442
}
418443

419444
for i, test := range tests {
@@ -429,7 +454,7 @@ func TestReferences(t *testing.T) {
429454
actual, err := server.References(ctx, &protocol.ReferenceParams{
430455
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
431456
TextDocument: protocol.TextDocumentIdentifier{
432-
URI: uri.URI("file://" + appDir + "/templates.templ"),
457+
URI: uri.URI("file://" + appDir + test.filename),
433458
},
434459
// Positions are zero indexed.
435460
Position: protocol.Position{

‎cmd/templ/lspcmd/proxy/server.go

+76-4
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@ package proxy
33
import (
44
"context"
55
"fmt"
6+
"os"
7+
"path/filepath"
68
"regexp"
79
"strings"
810

911
"github.com/a-h/parse"
1012
lsp "github.com/a-h/protocol"
13+
"go.lsp.dev/uri"
14+
"go.uber.org/zap"
15+
1116
"github.com/a-h/templ"
1217
"github.com/a-h/templ/cmd/templ/imports"
1318
"github.com/a-h/templ/generator"
1419
"github.com/a-h/templ/parser/v2"
15-
"go.lsp.dev/uri"
16-
"go.uber.org/zap"
1720
)
1821

1922
// Server is responsible for rewriting messages that are
@@ -37,6 +40,7 @@ type Server struct {
3740
DiagnosticCache *DiagnosticCache
3841
TemplSource *DocumentContents
3942
GoSource map[string]string
43+
preLoadURIs []*lsp.DidOpenTextDocumentParams
4044
}
4145

4246
func NewServer(log *zap.Logger, target lsp.Server, cache *SourceMapCache, diagnosticCache *DiagnosticCache) (s *Server) {
@@ -81,6 +85,7 @@ func (p *Server) convertTemplRangeToGoRange(templURI lsp.DocumentURI, input lsp.
8185
var sourceMap *parser.SourceMap
8286
sourceMap, ok = p.SourceMapCache.Get(string(templURI))
8387
if !ok {
88+
p.Log.Warn("templ->go: sourcemap not found in cache")
8489
return
8590
}
8691
// Map from the source position to target Go position.
@@ -101,6 +106,7 @@ func (p *Server) convertGoRangeToTemplRange(templURI lsp.DocumentURI, input lsp.
101106
output = input
102107
sourceMap, ok := p.SourceMapCache.Get(string(templURI))
103108
if !ok {
109+
p.Log.Warn("go->templ: sourcemap not found in cache")
104110
return
105111
}
106112
// Map from the source position to target Go position.
@@ -228,6 +234,62 @@ func (p *Server) Initialize(ctx context.Context, params *lsp.InitializeParams) (
228234
Save: &lsp.SaveOptions{IncludeText: true},
229235
}
230236

237+
for _, c := range params.WorkspaceFolders {
238+
path := strings.TrimPrefix(c.URI, "file://")
239+
werr := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
240+
if err != nil {
241+
return err
242+
}
243+
p.Log.Info("found file", zap.String("path", path))
244+
uri := uri.URI("file://" + path)
245+
isTemplFile, goURI := convertTemplToGoURI(uri)
246+
247+
if !isTemplFile {
248+
p.Log.Info("not a templ file", zap.String("uri", string(uri)))
249+
return nil
250+
}
251+
252+
b, err := os.ReadFile(path)
253+
if err != nil {
254+
return err
255+
}
256+
p.TemplSource.Set(string(uri), NewDocument(p.Log, string(b)))
257+
// Parse the template.
258+
template, ok, err := p.parseTemplate(ctx, uri, string(b))
259+
if err != nil {
260+
p.Log.Error("parseTemplate failure", zap.Error(err))
261+
}
262+
if !ok {
263+
p.Log.Info("parsing template did not succeed", zap.String("uri", string(uri)))
264+
return nil
265+
}
266+
w := new(strings.Builder)
267+
sm, _, err := generator.Generate(template, w)
268+
if err != nil {
269+
return fmt.Errorf("generate failure: %w", err)
270+
}
271+
p.Log.Info("setting source map cache contents", zap.String("uri", string(uri)))
272+
p.SourceMapCache.Set(string(uri), sm)
273+
// Set the Go contents.
274+
p.GoSource[string(uri)] = w.String()
275+
276+
didOpenParams := &lsp.DidOpenTextDocumentParams{
277+
TextDocument: lsp.TextDocumentItem{
278+
URI: goURI,
279+
Text: w.String(),
280+
Version: 1,
281+
LanguageID: "go",
282+
},
283+
}
284+
285+
p.preLoadURIs = append(p.preLoadURIs, didOpenParams)
286+
return nil
287+
})
288+
if werr != nil {
289+
p.Log.Error("walk error", zap.Error(werr))
290+
}
291+
}
292+
231293
result.ServerInfo.Name = "templ-lsp"
232294
result.ServerInfo.Version = templ.Version()
233295

@@ -237,7 +299,17 @@ func (p *Server) Initialize(ctx context.Context, params *lsp.InitializeParams) (
237299
func (p *Server) Initialized(ctx context.Context, params *lsp.InitializedParams) (err error) {
238300
p.Log.Info("client -> server: Initialized")
239301
defer p.Log.Info("client -> server: Initialized end")
240-
return p.Target.Initialized(ctx, params)
302+
goInitErr := p.Target.Initialized(ctx, params)
303+
304+
for i, doParams := range p.preLoadURIs {
305+
doErr := p.Target.DidOpen(ctx, doParams)
306+
if doErr != nil {
307+
return doErr
308+
}
309+
p.preLoadURIs[i] = nil
310+
}
311+
312+
return goInitErr
241313
}
242314

243315
func (p *Server) Shutdown(ctx context.Context) (err error) {
@@ -464,8 +536,8 @@ func getPackageFromItemDetail(pkg string) string {
464536
}
465537

466538
type importInsert struct {
467-
LineIndex int
468539
Text string
540+
LineIndex int
469541
}
470542

471543
var nonImportKeywordRegexp = regexp.MustCompile(`^(?:templ|func|css|script|var|const|type)\s`)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package main
2+
3+
templ Remote() {
4+
<p>This is remote content</p>
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
templ RemoteInclusionTest() {
4+
@Remote
5+
}
6+
7+
templ Remote2() {
8+
@Remote
9+
}

2 commit comments

Comments
 (2)

a-h commented on Sep 25, 2024

@a-h
Owner

There are incorrectly cased files in this commit. They will need to be fixed to remotechild.templ and remoteparent.templ.

joerdav commented on Sep 25, 2024

@joerdav
Collaborator

Good point, I missed that one

Please sign in to comment.