diff --git a/go.mod b/go.mod index b9f600d..0592d7d 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/gnolang/gno v0.0.0-20240118150545-7aa81d138701 github.com/google/go-github v17.0.0+incompatible github.com/orcaman/concurrent-map/v2 v2.0.1 + github.com/samber/slog-multi v1.1.0 github.com/spf13/cobra v1.5.0 go.lsp.dev/jsonrpc2 v0.10.0 go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 @@ -47,6 +48,7 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rs/cors v1.10.1 // indirect + github.com/samber/lo v1.38.1 // indirect github.com/segmentio/asm v1.1.3 // indirect github.com/segmentio/encoding v0.3.4 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -56,6 +58,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.15.0 // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.14.0 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/go.sum b/go.sum index 4ba0727..177943b 100644 --- a/go.sum +++ b/go.sum @@ -180,6 +180,10 @@ github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/samber/slog-multi v1.1.0 h1:m5wfpXE8Qu2gCiR/JnhFGsLcWDOmTxnso32EMffVAY0= +github.com/samber/slog-multi v1.1.0/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo= github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= github.com/segmentio/encoding v0.3.4 h1:WM4IBnxH8B9TakiM2QD5LyNl9JSndh88QbHqVC+Pauc= @@ -239,6 +243,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/internal/lsp/build.go b/internal/lsp/build.go index 8062401..4969bbf 100644 --- a/internal/lsp/build.go +++ b/internal/lsp/build.go @@ -30,6 +30,7 @@ func (s *server) TranspileAndBuild(file *GnoFile) ([]ErrorInfo, error) { return nil, err } + slog.Info("transpiling", slog.String("dir", tmpDir)) preOut, _ := tools.Transpile(tmpDir) slog.Info(string(preOut)) if len(preOut) > 0 { diff --git a/internal/lsp/check.go b/internal/lsp/check.go index 505b227..ef05f8b 100644 --- a/internal/lsp/check.go +++ b/internal/lsp/check.go @@ -7,10 +7,12 @@ import ( "go/parser" "go/token" "go/types" + "io/fs" "log/slog" "math" "os" "path/filepath" + "regexp" "sort" "strconv" "strings" @@ -38,6 +40,7 @@ type PackageGetter interface { // PackageInfo if found. // Note: it doesn't work for relative path func GetPackageInfo(path string) (*PackageInfo, error) { + //slog.Info("getting package info for", slog.String("path", path)) // if not absolute, assume its import path if !filepath.IsAbs(path) { if env.GlobalEnv.GNOROOT == "" { @@ -45,7 +48,47 @@ func GetPackageInfo(path string) (*PackageInfo, error) { // `examples` and `stdlibs` return nil, errors.New("GNOROOT not set") } - if strings.HasPrefix(path, "gno.land/") { // look in `examples` + if strings.HasPrefix(path, "gno.land/") { // look in gno `examples` and extra dirs + // find in extra dirs + srcPathsStr, ok := os.LookupEnv("GNOSRCPATHS") + //slog.Info("extra source paths", slog.String("env", srcPathsStr)) + if ok { + srcPaths := strings.Split(srcPathsStr, ",") + for _, srcPath := range srcPaths { + modFiles := []string{} + if err := filepath.Walk(srcPath, func(path string, info fs.FileInfo, err error) error { + if !strings.HasSuffix(path, "/gno.mod") { + return nil + } + //slog.Info("found mod file", slog.String("path", path)) + modFiles = append(modFiles, path) + return nil + }); err != nil { + return nil, err + } + + for _, modFile := range modFiles { + modData, err := os.ReadFile(modFile) + if err != nil { + continue + } + re := regexp.MustCompile(`^module (.+)`) + res := re.FindSubmatch(modData) + if len(res) != 2 { + continue + } + pkgDir := strings.TrimSuffix(modFile, "/gno.mod") + pkgPath := string(res[1]) + //slog.Info("found package", slog.String("path", pkgPath), slog.String("dir", pkgDir)) + if pkgPath != path { + continue + } + //slog.Info("found correct package") + return getPackageInfo(pkgDir) + } + } + } + path = filepath.Join(env.GlobalEnv.GNOROOT, "examples", path) } else { // look into `stdlibs` path = filepath.Join(env.GlobalEnv.GNOROOT, "gnovm", "stdlibs", path) diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go index c1f0ff0..ed582f7 100644 --- a/internal/lsp/completion.go +++ b/internal/lsp/completion.go @@ -41,6 +41,7 @@ func (cs *CompletionStore) lookupPkg(pkg string) *Package { } func (cs *CompletionStore) lookupSymbol(pkg, symbol string) *Symbol { + pnames := []string{} for _, p := range cs.pkgs { if p.Name == pkg { for _, s := range p.Symbols { @@ -49,7 +50,9 @@ func (cs *CompletionStore) lookupSymbol(pkg, symbol string) *Symbol { } } } + pnames = append(pnames, p.Name) } + slog.Info("pkg not found", "pkg", pkg, "symbol", symbol, "pkgs", pnames) return nil } diff --git a/internal/lsp/definition.go b/internal/lsp/definition.go index da64c23..2378c30 100644 --- a/internal/lsp/definition.go +++ b/internal/lsp/definition.go @@ -86,14 +86,17 @@ func (s *server) Definition(ctx context.Context, reply jsonrpc2.Replier, req jso int(line), offset, ) + slog.Info("node", "str", n.String(), "tv", tv) if tv == nil || tv.Type == nil { switch t := paths[1].(type) { case *ast.FuncDecl: + slog.Info("func decl", "t", t) if t.Recv != nil { return definitionMethodDecl(ctx, reply, params, pkg, n, t) } return definitionFuncDecl(ctx, reply, params, pkg, n) case *ast.SelectorExpr: + slog.Info("selector expr", "t", t) return definitionSelectorExpr(ctx, s, reply, params, pgf, pkg, paths, n, t, int(line)) default: return reply(ctx, nil, nil) @@ -170,6 +173,7 @@ func definitionSelectorExpr(ctx context.Context, s *server, reply jsonrpc2.Repli } else if last == parentStr { // on package symbol symbol := s.completionStore.lookupSymbol(parentStr, i.Name) if symbol == nil { + slog.Info("symbol not found", "parent", parentStr, "symbol", i.Name) break } diff --git a/internal/lsp/hover.go b/internal/lsp/hover.go index e9726a1..d133f17 100644 --- a/internal/lsp/hover.go +++ b/internal/lsp/hover.go @@ -64,17 +64,21 @@ func (s *server) Hover(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2 // Get path enclosing paths := pathEnclosingObjNode(pgf.File, token.Pos(offset)) if len(paths) < 2 { + slog.Info("path not found", "pos", token.Pos(offset)) return reply(ctx, nil, nil) } switch n := paths[0].(type) { case *ast.Ident: + slog.Info("ident type") + _, tv := getTypeAndValue( *pkg.TypeCheckResult.fset, info, n.Name, int(line), offset, ) + slog.Info("got tv", "tv", tv) if tv == nil || tv.Type == nil { switch t := paths[1].(type) { case *ast.FuncDecl: @@ -153,6 +157,7 @@ func (s *server) Hover(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2 ), }, nil) default: + slog.Info("unknown type") return reply(ctx, nil, nil) } } diff --git a/internal/lsp/server.go b/internal/lsp/server.go index f53453d..cd5064f 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -31,6 +31,7 @@ func BuildServerHandler(conn jsonrpc2.Conn, e *env.Env) jsonrpc2.Handler { if e.GNOROOT != "" { dirs = append(dirs, filepath.Join(e.GNOROOT, "examples")) dirs = append(dirs, filepath.Join(e.GNOROOT, "gnovm/stdlibs")) + dirs = append(dirs, os.Getenv("GNOSRCPATHS")) } server := &server{ conn: conn, diff --git a/internal/tools/build.go b/internal/tools/build.go index d48ff6f..d93f6b6 100644 --- a/internal/tools/build.go +++ b/internal/tools/build.go @@ -1,12 +1,21 @@ package tools import ( + "log/slog" + "os" "os/exec" "path/filepath" + "strings" ) // Build a Gno package: gno transpile -gobuild . // TODO: Remove this in the favour of directly using tools/transpile.go func Build(rootDir string) ([]byte, error) { - return exec.Command("gno", "transpile", "-skip-imports", "-gobuild", filepath.Join(rootDir)).CombinedOutput() + args := []string{"transpile", "-skip-imports", "-gobuild", filepath.Join(rootDir)} + srcPathsStr := os.Getenv("GNOSRCPATHS") + if srcPathsStr != "" { + args = append(args, "-extra-dirs", srcPathsStr) + } + slog.Info("will run", slog.String("cmd", strings.Join(append([]string{"gno"}, args...), " "))) + return exec.Command("gno", args...).CombinedOutput() } diff --git a/internal/tools/transpile.go b/internal/tools/transpile.go index 4ff66de..e79aef9 100644 --- a/internal/tools/transpile.go +++ b/internal/tools/transpile.go @@ -1,11 +1,17 @@ package tools import ( + "os" "os/exec" "path/filepath" ) // Transpile a Gno package: gno transpile . func Transpile(rootDir string) ([]byte, error) { - return exec.Command("gno", "transpile", "-skip-imports", filepath.Join(rootDir)).CombinedOutput() + args := []string{"transpile", "-skip-imports", filepath.Join(rootDir)} + srcPathsStr := os.Getenv("GNOSRCPATHS") + if srcPathsStr != "" { + args = append(args, "-extra-dirs", srcPathsStr) + } + return exec.Command("gno", args...).CombinedOutput() } diff --git a/main.go b/main.go index 1a5e871..b992f82 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,31 @@ package main -import "github.com/gnolang/gnopls/cmd" +import ( + "log/slog" + "os" + "path/filepath" + + "github.com/gnolang/gnopls/cmd" + slogmulti "github.com/samber/slog-multi" +) func main() { + userHomeDir, err := os.UserHomeDir() + if err != nil { + panic(err) + } + logFilePath := filepath.Join(userHomeDir, "gnopls.log") + logFile, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) + if err != nil { + panic(err) + } + defer logFile.Close() + logger := slog.New( + slogmulti.Fanout( + slog.NewTextHandler(logFile, &slog.HandlerOptions{}), // pass to first handler: logstash over tcp + slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{}), // then to second handler: stderr + ), + ) + slog.SetDefault(logger) cmd.Execute() }