Skip to content

Commit

Permalink
lsp: push and clear template syntax errors to the client, closes #5
Browse files Browse the repository at this point in the history
  • Loading branch information
a-h committed May 29, 2021
1 parent 18a2128 commit 0654d88
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 8 deletions.
51 changes: 49 additions & 2 deletions cmd/lspcmd/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func NewProxy(logger *zap.Logger) (p *Proxy) {
sourceMapCache: newSourceMapCache(),
// Prevent trying to send to the client when message handling is taking place.
// The proxy can place up to 32 requests onto the toClient buffered channel
// during handling. They're processed when the clientInUse mutex is released.
// during handling.
toClient: make(chan toClientRequest, 32),
}
}
Expand All @@ -45,10 +45,12 @@ func (p *Proxy) Init(ctx context.Context, client, gopls *jsonrpc2.Conn) {
p.client = client
p.gopls = gopls
go func() {
p.log.Info("sendToClient: starting up")
for r := range p.toClient {
r := r
p.sendToClient(r)
}
p.log.Info("sendToClient: closed")
}()
}

Expand All @@ -61,7 +63,7 @@ type toClientRequest struct {
// sendToClient should not be called directly. Instead, send a message to the non-blocking
// toClient channel.
func (p *Proxy) sendToClient(r toClientRequest) {
p.log.Info("sendToClient: starting", zap.String("method", r.Method))
p.log.Info("sendToClient: sending", zap.String("method", r.Method))
if r.Notif {
err := p.client.Notify(p.context, r.Method, r.Params)
if err != nil {
Expand Down Expand Up @@ -290,8 +292,10 @@ func (p *Proxy) rewriteDidOpenRequest(r *jsonrpc2.Request) (err error) {
// Parse the template.
template, err := templ.ParseString(params.TextDocument.Text)
if err != nil {
p.sendParseErrorDiagnosticNotifications(params.TextDocument.URI, err)
return
}
p.sendDiagnosticClearNotification(params.TextDocument.URI)
// Generate the output code and cache the source map and Go contents to use during completion
// requests.
w := new(strings.Builder)
Expand Down Expand Up @@ -341,8 +345,10 @@ func (p *Proxy) rewriteDidChangeRequest(ctx context.Context, r *jsonrpc2.Request
// Update the Go code.
template, err := templ.ParseString(string(templateText))
if err != nil {
p.sendParseErrorDiagnosticNotifications(params.TextDocument.URI, err)
return
}
p.sendDiagnosticClearNotification(params.TextDocument.URI)
w := new(strings.Builder)
sm, err := generator.Generate(template, w)
if err != nil {
Expand All @@ -368,6 +374,47 @@ func (p *Proxy) rewriteDidChangeRequest(ctx context.Context, r *jsonrpc2.Request
return
}

func (p *Proxy) sendDiagnosticClearNotification(uri lsp.DocumentURI) {
p.toClient <- toClientRequest{
Method: "textDocument/publishDiagnostics",
Notif: true,
Params: lsp.PublishDiagnosticsParams{
URI: uri,
Diagnostics: []lsp.Diagnostic{},
},
}
}

func (p *Proxy) sendParseErrorDiagnosticNotifications(uri lsp.DocumentURI, err error) {
pe, ok := err.(templ.ParseError)
if !ok {
return
}
p.toClient <- toClientRequest{
Method: "textDocument/publishDiagnostics",
Notif: true,
Params: lsp.PublishDiagnosticsParams{
URI: uri,
Diagnostics: []lsp.Diagnostic{
{
Range: lsp.Range{
Start: templatePositionToLSPPosition(pe.From),
End: templatePositionToLSPPosition(pe.To),
},
Severity: lsp.Error,
Code: "",
Source: "templ",
Message: pe.Message,
},
},
},
}
}

func templatePositionToLSPPosition(p templ.Position) lsp.Position {
return lsp.Position{Line: p.Line - 1, Character: p.Col + 1}
}

func (p *Proxy) rewriteDidSaveRequest(r *jsonrpc2.Request) (err error) {
// Unmarshal the params.
var params lsp.DidSaveTextDocumentParams
Expand Down
2 changes: 1 addition & 1 deletion elementparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (p elementOpenCloseParser) Parse(pi parse.Input) parse.Result {
from := NewPositionFromInput(pi)
tnpr := newTemplateNodeParser().Parse(pi)
if !tnpr.Success {
if _, isParseError := tnpr.Error.(parseError); isParseError {
if _, isParseError := tnpr.Error.(ParseError); isParseError {
return tnpr
}
return parse.Failure("elementOpenCloseParser", newParseError(fmt.Sprintf("<%s>: %v", r.Name, tnpr.Error), from, NewPositionFromInput(pi)))
Expand Down
2 changes: 1 addition & 1 deletion packageparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestPackageParserErrors(t *testing.T) {
var tests = []struct {
name string
input string
expected parseError
expected ParseError
}{
{
name: "unterminated package",
Expand Down
9 changes: 5 additions & 4 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,22 @@ func (p templateParser) Parse(pi parse.Input) parse.Result {
}

// Parse error.
func newParseError(msg string, from Position, to Position) parseError {
return parseError{
func newParseError(msg string, from Position, to Position) ParseError {
return ParseError{
Message: msg,
From: from,
To: to,
}
}

type parseError struct {
// ParseError details where the error occurred in the file.
type ParseError struct {
Message string
From Position
To Position
}

func (pe parseError) Error() string {
func (pe ParseError) Error() string {
return fmt.Sprintf("%v at %v", pe.Message, pe.From)
}

Expand Down

0 comments on commit 0654d88

Please sign in to comment.