Skip to content
This repository was archived by the owner on Jan 31, 2025. It is now read-only.

Commit 336b382

Browse files
authored
fix: transpile step (#12)
* fix: transpile step - remove the unecessary tools.Build step - add GO111MODULE=off to remove the error related to the absence of go.mod - filter error correctly according to file notification - see NOTEs for more contextualized infos * chore: reduce log severity
1 parent 944349f commit 336b382

File tree

6 files changed

+92
-58
lines changed

6 files changed

+92
-58
lines changed

internal/lsp/build.go

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,47 +20,49 @@ type ErrorInfo struct {
2020
Tool string
2121
}
2222

23-
func (s *server) TranspileAndBuild(file *GnoFile) ([]ErrorInfo, error) {
24-
pkgDir := filepath.Dir(file.URI.Filename())
25-
pkgName := filepath.Base(pkgDir)
26-
tmpDir := filepath.Join(s.env.GNOHOME, "gnopls", "tmp", pkgName)
23+
func (s *server) Transpile() ([]ErrorInfo, error) {
24+
moduleName := filepath.Base(s.workspaceFolder)
25+
tmpDir := filepath.Join(s.env.GNOHOME, "gnopls", "tmp", moduleName)
2726

28-
err := copyDir(pkgDir, tmpDir)
27+
err := copyDir(s.workspaceFolder, tmpDir)
2928
if err != nil {
3029
return nil, err
3130
}
3231

33-
preOut, _ := tools.Transpile(tmpDir)
34-
slog.Info(string(preOut))
32+
preOut, errTranspile := tools.Transpile(tmpDir)
3533
if len(preOut) > 0 {
36-
return parseErrors(file, string(preOut), "transpile")
34+
// parse errors even if errTranspile!=nil bc that's always the case if
35+
// there's errors to parse.
36+
slog.Info("transpile error", "out", string(preOut), "err", errTranspile)
37+
errors, errParse := s.parseErrors(string(preOut), "transpile")
38+
if errParse != nil {
39+
return nil, errParse
40+
}
41+
if len(errors) == 0 && errTranspile != nil {
42+
// no parsed errors but errTranspile!=nil, this is an unexpected error.
43+
// (for example the gno binary was not found)
44+
return nil, errTranspile
45+
}
46+
return errors, nil
3747
}
38-
39-
buildOut, _ := tools.Build(tmpDir)
40-
slog.Info(string(buildOut))
41-
return parseErrors(file, string(buildOut), "build")
48+
return nil, nil
4249
}
4350

4451
// This is used to extract information from the `gno build` command
4552
// (see `parseError` below).
4653
//
4754
// TODO: Maybe there's a way to get this in a structured format?
55+
// -> will be available in go1.24 with `go build -json`
4856
var errorRe = regexp.MustCompile(`(?m)^([^#]+?):(\d+):(\d+):(.+)$`)
4957

50-
// parseErrors parses the output of the `gno build` command for errors.
51-
//
52-
// They look something like this:
58+
// parseErrors parses the output of the `gno transpile -gobuild` command for
59+
// errors.
5360
//
61+
// The format is:
5462
// ```
55-
// command-line-arguments
56-
// # command-line-arguments
57-
// <file>:20:9: undefined: strin
58-
//
59-
// <pkg_path>: build pkg: std go compiler: exit status 1
60-
//
61-
// 1 go build errors
63+
// <file.gno>:<line>:<col>: <error>
6264
// ```
63-
func parseErrors(file *GnoFile, output, cmd string) ([]ErrorInfo, error) {
65+
func (s *server) parseErrors(output, cmd string) ([]ErrorInfo, error) {
6466
errors := []ErrorInfo{}
6567

6668
matches := errorRe.FindAllStringSubmatch(output, -1)
@@ -69,24 +71,39 @@ func parseErrors(file *GnoFile, output, cmd string) ([]ErrorInfo, error) {
6971
}
7072

7173
for _, match := range matches {
74+
path := match[1]
7275
line, err := strconv.Atoi(match[2])
7376
if err != nil {
74-
return nil, err
77+
return nil, fmt.Errorf("parseErrors '%s': %w", match, err)
7578
}
7679

7780
column, err := strconv.Atoi(match[3])
7881
if err != nil {
79-
return nil, err
82+
return nil, fmt.Errorf("parseErrors '%s': %w", match, err)
8083
}
81-
slog.Info("parsing", "line", line, "column", column, "msg", match[4])
82-
83-
errorInfo := findError(file, match[1], line, column, match[4], cmd)
84-
errors = append(errors, errorInfo)
84+
msg := strings.TrimSpace(match[4])
85+
slog.Debug("parseErrors", "path", path, "line", line, "column", column, "msg", msg)
86+
errors = append(errors, ErrorInfo{
87+
FileName: filepath.Join(s.workspaceFolder, path),
88+
Line: line,
89+
Column: column,
90+
Span: []int{column, column},
91+
Msg: msg,
92+
Tool: cmd,
93+
})
8594
}
8695

8796
return errors, nil
8897
}
8998

99+
// NOTE(tb): This function tries to guess the column start and end of the
100+
// error, by splitting the line into tokens (space separated). While this
101+
// might work most of the time, in some cases it might not, so I think
102+
// it is preferable not to try to guess and just return:
103+
// column_start = column_end
104+
// just like the output of gno transpile which only returns the start of the
105+
// column. This is why this function is no longer invoked in parseError.
106+
//
90107
// findError finds the error in the document, shifting the line and column
91108
// numbers to account for the header information in the generated Go file.
92109
func findError(file *GnoFile, fname string, line, col int, msg string, tool string) ErrorInfo {

internal/lsp/diagnostics.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,40 @@ package lsp
22

33
import (
44
"context"
5-
"path/filepath"
6-
"strings"
75

86
"go.lsp.dev/jsonrpc2"
97
"go.lsp.dev/protocol"
108
)
119

1210
func (s *server) getTranspileDiagnostics(file *GnoFile) ([]protocol.Diagnostic, error) {
13-
errors, err := s.TranspileAndBuild(file)
11+
errors, err := s.Transpile()
1412
if err != nil {
1513
return nil, err
1614
}
1715

18-
if pkg, ok := s.cache.pkgs.Get(filepath.Dir(string(file.URI.Filename()))); ok {
19-
filename := filepath.Base(file.URI.Filename())
20-
for _, er := range pkg.TypeCheckResult.Errors() {
21-
// Skip errors from other files in the same package
22-
if !strings.HasSuffix(er.FileName, filename) {
23-
continue
16+
/*
17+
NOTE(tb): not sure we really need this, bc Transpile already returns
18+
all the errors we need. The ones in the cache are just duplicates or
19+
potentially obsolete.
20+
21+
if pkg, ok := s.cache.pkgs.Get(filepath.Dir(string(file.URI.Filename()))); ok {
22+
filename := filepath.Base(file.URI.Filename())
23+
for _, er := range pkg.TypeCheckResult.Errors() {
24+
// Skip errors from other files in the same package
25+
if !strings.HasSuffix(er.FileName, filename) {
26+
continue
27+
}
28+
errors = append(errors, er)
2429
}
25-
errors = append(errors, er)
2630
}
27-
}
31+
*/
2832

2933
diagnostics := make([]protocol.Diagnostic, 0) // Init required for JSONRPC to send an empty array
3034
for _, er := range errors {
35+
if file.URI.Filename() != er.FileName {
36+
// Ignore error thay does not target file
37+
continue
38+
}
3139
diagnostics = append(diagnostics, protocol.Diagnostic{
3240
Range: *posToRange(er.Line, er.Span),
3341
Severity: protocol.DiagnosticSeverityError,

internal/lsp/server.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ type server struct {
2323
completionStore *CompletionStore
2424
cache *Cache
2525

26-
initialized atomic.Bool
26+
initialized atomic.Bool
27+
workspaceFolder string
2728
}
2829

2930
func BuildServerHandler(conn jsonrpc2.Conn, e *env.Env) jsonrpc2.Handler {
@@ -90,6 +91,15 @@ func (s *server) Initialize(ctx context.Context, reply jsonrpc2.Replier, req jso
9091
if err := json.Unmarshal(req.Params(), &params); err != nil {
9192
return sendParseError(ctx, reply, err)
9293
}
94+
if len(params.WorkspaceFolders) > 0 {
95+
s.workspaceFolder = params.WorkspaceFolders[0].Name
96+
}
97+
if s.workspaceFolder == "" {
98+
// Not all LSP client have migrated to the latest WorkspaceFolders, some
99+
// like vim-lsp are still using the deprecated one RootUri.
100+
s.workspaceFolder = params.RootURI.Filename() //nolint:staticcheck
101+
}
102+
slog.Info("Initialize", "params", params, "workspaceFolder", s.workspaceFolder)
93103

94104
return reply(ctx, protocol.InitializeResult{
95105
ServerInfo: &protocol.ServerInfo{

internal/tools/build.go

Lines changed: 0 additions & 12 deletions
This file was deleted.

internal/tools/transpile.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
package tools
22

33
import (
4+
"fmt"
5+
"os"
46
"os/exec"
57
"path/filepath"
8+
"strings"
69
)
710

811
// Transpile a Gno package: gno transpile <dir>.
912
func Transpile(rootDir string) ([]byte, error) {
10-
return exec.Command("gno", "transpile", "-skip-imports", filepath.Join(rootDir)).CombinedOutput()
13+
cmd := exec.Command("gno", "transpile", "-skip-imports", "-gobuild", filepath.Join(rootDir))
14+
// FIXME(tb): See https://github.com/gnolang/gno/pull/1695/files#r1697255524
15+
const disableGoMod = "GO111MODULE=off"
16+
cmd.Env = append(os.Environ(), disableGoMod)
17+
bz, err := cmd.CombinedOutput()
18+
if err != nil {
19+
return bz, fmt.Errorf("running '%s': %w: %s", strings.Join(cmd.Args, " "), err, string(bz))
20+
}
21+
return bz, nil
1122
}

testdata/document_open.txtar

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ func Bye() {
7070
},
7171
"end": {
7272
"line": 2,
73-
"character": 4294967294
73+
"character": 6
7474
}
7575
},
7676
"severity": 1,
77-
"code": "typecheck",
77+
"code": "transpile",
7878
"source": "gnopls",
7979
"message": "undefined: X"
8080
},
@@ -86,11 +86,11 @@ func Bye() {
8686
},
8787
"end": {
8888
"line": 5,
89-
"character": 4294967294
89+
"character": 1
9090
}
9191
},
9292
"severity": 1,
93-
"code": "typecheck",
93+
"code": "transpile",
9494
"source": "gnopls",
9595
"message": "declared and not used: y"
9696
}

0 commit comments

Comments
 (0)