Skip to content

Commit dc9dbde

Browse files
authored
Merge branch 'cloudwego:main' into main
2 parents 7ac1415 + ab6d6b7 commit dc9dbde

File tree

5 files changed

+149
-29
lines changed

5 files changed

+149
-29
lines changed

.github/workflows/regression.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@ jobs:
1313
runs-on: ubuntu-latest
1414
#if: "!contains(github.event.pull_request.title, '[NO-REGRESSION-TEST]')"
1515
env:
16-
LANGS: "go rust python java typescript"
17-
DIFFJSON_IGNORE: " ['id'] ['Path'] "
16+
LANGS: "go rust python typescript"
17+
# ignore package version for Go e.g. 'a.b/c@506fb8ece467f3a71c29322169bef9b0bc92d554'
18+
DIFFJSON_IGNORE: >
19+
['id']
20+
['Path']
21+
['Modules']['a.b/c']['Dependencies']['a.b/c']
22+
['Modules']['a.b/c/cmdx']['Dependencies']['a.b/c/cmdx']
1823
steps:
1924
- name: Checkout pull request code
2025
uses: actions/checkout@v4

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ project, kindly check out our guide:
145145
146146
&ensp;&ensp;&ensp; <img src="images/lark_group_zh.png" alt="LarkGroup" width="200"/>
147147
148+
# Contributors
149+
Thank you for your contribution to ABCoder!
150+
151+
[![Contributors](https://contrib.rocks/image?repo=cloudwego/abcoder)](https://github.com/cloudwego/abcoder/graphs/contributors)
148152
149153
# License
150154
This project is licensed under the [Apache-2.0 License](LICENSE-APACHE).

lang/golang/parser/parser.go

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package parser
1717
import (
1818
"bufio"
1919
"bytes"
20+
"encoding/json"
2021
"fmt"
2122
"go/ast"
2223
"go/parser"
@@ -94,12 +95,11 @@ func newGoParser(name string, homePageDir string, opts Options) *GoParser {
9495
}
9596

9697
func (p *GoParser) collectGoMods(startDir string) error {
97-
9898
err := filepath.Walk(startDir, func(path string, info fs.FileInfo, err error) error {
9999
if err != nil || !strings.HasSuffix(path, "go.mod") {
100100
return nil
101101
}
102-
name, content, err := getModuleName(path)
102+
name, _, err := getModuleName(path)
103103
if err != nil {
104104
return err
105105
}
@@ -109,7 +109,8 @@ func (p *GoParser) collectGoMods(startDir string) error {
109109
}
110110
p.repo.Modules[name] = newModule(name, rel)
111111
p.modules = append(p.modules, newModuleInfo(name, rel, name))
112-
deps, err := parseModuleFile(content)
112+
113+
deps, err := getDeps(filepath.Dir(path))
113114
if err != nil {
114115
return err
115116
}
@@ -126,6 +127,75 @@ func (p *GoParser) collectGoMods(startDir string) error {
126127
return nil
127128
}
128129

130+
type replace struct {
131+
Path string `json:"Path"`
132+
Version string `json:"Version"`
133+
Dir string `json:"Dir"`
134+
GoMod string `json:"GoMod"`
135+
}
136+
137+
type dep struct {
138+
Module struct {
139+
Path string `json:"Path"`
140+
Version string `json:"Version"`
141+
Replace *replace `json:"Replace,omitempty"`
142+
Indirect bool `json:"Indirect"`
143+
Dir string `json:"Dir"`
144+
GoMod string `json:"GoMod"`
145+
} `json:"Module"`
146+
}
147+
148+
func getDeps(dir string) (map[string]string, error) {
149+
// run go mod tidy first to ensure all dependencies are resolved
150+
cmd := exec.Command("go", "mod", "tidy")
151+
cmd.Dir = dir
152+
output, err := cmd.CombinedOutput()
153+
if err != nil {
154+
return nil, fmt.Errorf("failed to execute 'go mod tidy', err: %v, output: %s", err, string(output))
155+
}
156+
157+
cmd = exec.Command("go", "list", "-json", "all")
158+
cmd.Dir = dir
159+
output, err = cmd.CombinedOutput()
160+
if err != nil {
161+
return nil, fmt.Errorf("failed to execute 'go list -json all', err: %v, output: %s", err, string(output))
162+
}
163+
164+
deps := make(map[string]string)
165+
decoder := json.NewDecoder(bytes.NewReader(output))
166+
for {
167+
var mod dep
168+
if err := decoder.Decode(&mod); err != nil {
169+
if err.Error() == "EOF" {
170+
break
171+
}
172+
return nil, fmt.Errorf("failed to decode json: %v", err)
173+
}
174+
module := mod.Module
175+
// golang internal package, ignore it.
176+
if module.Path == "" {
177+
continue
178+
}
179+
if module.Replace != nil {
180+
deps[module.Path] = module.Replace.Path + "@" + module.Replace.Version
181+
} else {
182+
if module.Version != "" {
183+
deps[module.Path] = module.Path + "@" + module.Version
184+
} else {
185+
// If no version, it's a local package. So we use local commit as version
186+
commit, err := getCommitHash(dir)
187+
if err != nil {
188+
deps[module.Path] = module.Path
189+
} else {
190+
deps[module.Path] = module.Path + "@" + commit
191+
}
192+
}
193+
}
194+
}
195+
196+
return deps, nil
197+
}
198+
129199
// ParseRepo parse the entiry repo from homePageDir recursively until end
130200
func (p *GoParser) ParseRepo() (Repository, error) {
131201
for _, lib := range p.modules {

lang/golang/parser/utils.go

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ import (
2424
"go/types"
2525
"io"
2626
"os"
27+
"os/exec"
2728
"path"
2829
"regexp"
2930
"strings"
3031
"sync"
3132

3233
"github.com/Knetic/govaluate"
3334
. "github.com/cloudwego/abcoder/lang/uniast"
34-
"golang.org/x/mod/modfile"
3535
)
3636

3737
func shouldIgnoreDir(path string) bool {
@@ -181,26 +181,6 @@ func getModuleName(modFilePath string) (string, []byte, error) {
181181
return "", data, nil
182182
}
183183

184-
// parse go.mod and get a map of module name to module_path@version
185-
func parseModuleFile(data []byte) (map[string]string, error) {
186-
ast, err := modfile.Parse("go.mod", data, nil)
187-
if err != nil {
188-
return nil, fmt.Errorf("failed to parse go.mod file: %v", err)
189-
}
190-
modules := make(map[string]string)
191-
for _, req := range ast.Require {
192-
// if req.Indirect {
193-
// continue
194-
// }
195-
modules[req.Mod.Path] = req.Mod.Path + "@" + req.Mod.Version
196-
}
197-
// replaces
198-
for _, replace := range ast.Replace {
199-
modules[replace.Old.Path] = replace.New.Path + "@" + replace.New.Version
200-
}
201-
return modules, nil
202-
}
203-
204184
func isGoBuiltins(name string) bool {
205185
switch name {
206186
case "append", "cap", "close", "complex", "copy", "delete", "imag", "len", "make", "new", "panic", "print", "println", "real", "recover":
@@ -337,3 +317,13 @@ func newIdentity(mod, pkg, name string) Identity {
337317
func isUpperCase(c byte) bool {
338318
return c >= 'A' && c <= 'Z'
339319
}
320+
321+
func getCommitHash(dir string) (string, error) {
322+
cmd := exec.Command("git", "rev-parse", "HEAD")
323+
cmd.Dir = dir
324+
output, err := cmd.Output()
325+
if err != nil {
326+
return "", fmt.Errorf("failed to get commit hash: %v", err)
327+
}
328+
return strings.TrimSpace(string(output)), nil
329+
}

lang/rust/utils/lsp.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package utils
1616

1717
import (
1818
"context"
19+
"fmt"
1920
"path/filepath"
2021
"regexp"
2122
"strings"
@@ -46,6 +47,23 @@ func GetLSPClient(root string) *lsp.LSPClient {
4647
return cli
4748
}
4849

50+
// 查找符号对应的代码
51+
// root: 项目根目录
52+
// file: 文件路径
53+
// mod: 命名空间,空表示本文件根
54+
// name: 符号名
55+
// receiver: method接收者,为空表示不是method
56+
func GetRawSymbol(root, file, mod, name string, receiver string, caseInsensitive bool) *lsp.DocumentSymbol {
57+
cli := GetLSPClient(root)
58+
sym, err := getSymbol(cli, root, file, mod, name, receiver, caseInsensitive)
59+
if err != nil {
60+
log.Error("get symbol for %s failed, err: %v", name, err)
61+
return nil
62+
}
63+
64+
return sym
65+
}
66+
4967
// 查找符号对应的代码
5068
// root: 项目根目录
5169
// file: 文件路径
@@ -54,10 +72,23 @@ func GetLSPClient(root string) *lsp.LSPClient {
5472
// receiver: method接收者,为空表示不是method
5573
func GetSymbol(root, file, mod, name string, receiver string, caseInsensitive bool) string {
5674
cli := GetLSPClient(root)
57-
syms, err := cli.FileStructure(context.Background(), lsp.NewURI(file))
75+
sym, err := getSymbol(cli, root, file, mod, name, receiver, caseInsensitive)
5876
if err != nil {
77+
log.Error("get symbol for %s failed, err: %v", name, err)
5978
return ""
6079
}
80+
if sym == nil {
81+
return ""
82+
}
83+
text, _ := cli.Locate(sym.Location)
84+
return text
85+
}
86+
87+
func getSymbol(cli *lsp.LSPClient, root, file, mod, name string, receiver string, caseInsensitive bool) (*lsp.DocumentSymbol, error) {
88+
syms, err := cli.FileStructure(context.Background(), lsp.NewURI(file))
89+
if err != nil {
90+
return nil, err
91+
}
6192

6293
var sym *lsp.DocumentSymbol
6394
if mod != "" {
@@ -91,10 +122,30 @@ func GetSymbol(root, file, mod, name string, receiver string, caseInsensitive bo
91122

92123
finally:
93124
if sym == nil {
94-
return ""
125+
return nil, fmt.Errorf("can not find symbol for %s", name)
126+
}
127+
128+
return sym, nil
129+
}
130+
131+
// 查找符号对应的源码以及文件行号
132+
// root: 项目根目录
133+
// file: 文件路径
134+
// mod: 命名空间,空表示本文件根
135+
// name: 符号名
136+
// receiver: method接收者,为空表示不是method
137+
func GetSymbolContentAndLocation(root, file, mod, name string, receiver string, caseInsensitive bool) (string, [2]int) {
138+
cli := GetLSPClient(root)
139+
sym, err := getSymbol(cli, root, file, mod, name, receiver, caseInsensitive)
140+
if err != nil {
141+
log.Error("get symbol for %s failed, err: %v", name, err)
142+
return "", [2]int{}
143+
}
144+
if sym == nil {
145+
return "", [2]int{}
95146
}
96147
text, _ := cli.Locate(sym.Location)
97-
return text
148+
return text, [2]int{sym.Location.Range.Start.Line, sym.Location.Range.End.Line}
98149
}
99150

100151
// NOTICE: 为了提供容错率,这里只是简单查找是否包含token,不做严格的标识符检查

0 commit comments

Comments
 (0)