Skip to content

Commit ea3b731

Browse files
committed
feat: record offset
1 parent e01828a commit ea3b731

File tree

8 files changed

+212
-79
lines changed

8 files changed

+212
-79
lines changed

src/compress/golang/plugin/parse/ctx.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ type fileContext struct {
4747
func (ctx *fileContext) FileLine(node ast.Node) FileLine {
4848
pos := ctx.fset.Position((node).Pos())
4949
rel, _ := filepath.Rel(ctx.repoDir, pos.Filename)
50-
return FileLine{File: rel, Line: pos.Line}
50+
end := ctx.fset.Position((node).End())
51+
return FileLine{File: rel, Line: pos.Line, StartOffset: pos.Offset, EndOffset: end.Offset}
5152
}
5253

5354
func isExternalID(id *Identity, curmod string) bool {

src/lang/collect/export.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,19 @@ type dependency struct {
3232
Symbol *DocumentSymbol `json:"symbol"`
3333
}
3434

35-
func (d dependency) FileLine() uniast.FileLine {
35+
func (c *Collector) fileLine(loc Location) uniast.FileLine {
36+
var rel string
37+
if c.internal(loc) {
38+
rel, _ = filepath.Rel(c.repo, loc.URI.File())
39+
} else {
40+
rel = filepath.Base(loc.URI.File())
41+
}
42+
text := c.cli.GetFile(loc.URI).Text
3643
return uniast.FileLine{
37-
File: d.Location.URI.File(),
38-
Line: d.Location.Range.Start.Line,
44+
File: rel,
45+
Line: loc.Range.Start.Line + 1,
46+
StartOffset: lsp.PositionOffset(text, loc.Range.Start),
47+
EndOffset: lsp.PositionOffset(text, loc.Range.End),
3948
}
4049
}
4150

@@ -129,10 +138,7 @@ func (c *Collector) exportSymbol(repo *uniast.Repository, symbol *DocumentSymbol
129138
} else {
130139
relfile = filepath.Base(file)
131140
}
132-
fileLine := uniast.FileLine{
133-
File: relfile,
134-
Line: symbol.Location.Range.Start.Line + 1,
135-
}
141+
fileLine := c.fileLine(symbol.Location)
136142
// collect files
137143
if module.Files[relfile] == nil {
138144
module.Files[relfile] = uniast.NewFile(relfile)
@@ -171,7 +177,7 @@ func (c *Collector) exportSymbol(repo *uniast.Repository, symbol *DocumentSymbol
171177
log.Error("export input symbol %s failed: %v\n", input.Symbol, err)
172178
continue
173179
}
174-
dep := uniast.NewDependency(*tyid, input.FileLine())
180+
dep := uniast.NewDependency(*tyid, c.fileLine(input.Location))
175181
obj.Types = uniast.Dedup(obj.Types, dep)
176182
}
177183
}
@@ -183,7 +189,7 @@ func (c *Collector) exportSymbol(repo *uniast.Repository, symbol *DocumentSymbol
183189
log.Error("export input symbol %s failed: %v\n", input.Symbol, err)
184190
continue
185191
}
186-
dep := uniast.NewDependency(*tyid, input.FileLine())
192+
dep := uniast.NewDependency(*tyid, c.fileLine(input.Location))
187193
obj.Params = uniast.Dedup(obj.Params, dep)
188194
}
189195
}
@@ -195,7 +201,7 @@ func (c *Collector) exportSymbol(repo *uniast.Repository, symbol *DocumentSymbol
195201
log.Error("export output symbol %s failed: %v\n", output.Symbol, err)
196202
continue
197203
}
198-
dep := uniast.NewDependency(*tyid, output.FileLine())
204+
dep := uniast.NewDependency(*tyid, c.fileLine(output.Location))
199205
obj.Results = uniast.Dedup(obj.Results, dep)
200206
}
201207
}
@@ -244,7 +250,7 @@ func (c *Collector) exportSymbol(repo *uniast.Repository, symbol *DocumentSymbol
244250
log.Error("export dep symbol %s failed: %v\n", dep.Symbol, err)
245251
continue
246252
}
247-
pdep := uniast.NewDependency(*depid, dep.FileLine())
253+
pdep := uniast.NewDependency(*depid, c.fileLine(dep.Location))
248254
switch dep.Symbol.Kind {
249255
case lsp.SKFunction:
250256
obj.FunctionCalls = uniast.Dedup(obj.FunctionCalls, pdep)
@@ -291,7 +297,7 @@ func (c *Collector) exportSymbol(repo *uniast.Repository, symbol *DocumentSymbol
291297
}
292298
switch dep.Symbol.Kind {
293299
case lsp.SKStruct, lsp.SKTypeParameter, lsp.SKInterface, lsp.SKEnum:
294-
obj.SubStruct = append(obj.SubStruct, uniast.NewDependency(*depid, dep.FileLine()))
300+
obj.SubStruct = append(obj.SubStruct, uniast.NewDependency(*depid, c.fileLine(dep.Location)))
295301
default:
296302
log.Error("dep symbol %s not collected for \n", dep.Symbol, id)
297303
}

src/lang/lsp/client_test.go

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// Copyright 2025 CloudWeGo Authors
2-
//
2+
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
55
// You may obtain a copy of the License at
6-
//
6+
//
77
// https://www.apache.org/licenses/LICENSE-2.0
8-
//
8+
//
99
// Unless required by applicable law or agreed to in writing, software
1010
// distributed under the License is distributed on an "AS IS" BASIS,
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -19,6 +19,7 @@ import (
1919
"encoding/json"
2020
"fmt"
2121
"os"
22+
"path/filepath"
2223
"sync"
2324
"testing"
2425
"time"
@@ -28,56 +29,67 @@ import (
2829

2930
var golangLSP *LSPClient
3031
var rustLSP *LSPClient
31-
var rootDir = "/Users/bytedance/GOPATH/work/abcoder/testdata"
32+
var rootDir = "../../../testdata"
3233

33-
func TestMain(m *testing.M) {
34-
log.SetLogLevel(log.DebugLevel)
34+
func testClientInit(t *testing.T) {
3535
var err error
36-
golangLSP, err = NewLSPClient(rootDir+"/golang", "", 0, ClientOptions{
37-
Server: "gopls",
38-
Language: "go",
39-
Verbose: true,
40-
})
36+
rootDir, err = filepath.Abs(rootDir)
4137
if err != nil {
42-
fmt.Printf("Failed to initialize golang LSP client: %v", err)
43-
os.Exit(1)
38+
t.Fatalf("Failed to get absolute path of testdata: %v", err)
4439
}
40+
sync.OnceFunc(func() {
4541

46-
wg := sync.WaitGroup{}
47-
wg.Add(2)
48-
go func() {
49-
defer wg.Done()
50-
rustLSP, err = NewLSPClient("/root/codes/abcoder/tmp/lust-example-item", "/root/codes/abcoder/tmp/lust-example-item/src/lib.rs", time.Second*30, ClientOptions{
51-
Server: "rust-analyzer",
52-
Language: "rust",
42+
log.SetLogLevel(log.DebugLevel)
43+
var err error
44+
golangLSP, err = NewLSPClient(rootDir+"/golang", "", 0, ClientOptions{
45+
Server: "gopls",
46+
Language: "go",
5347
Verbose: true,
5448
})
5549
if err != nil {
56-
fmt.Printf("Failed to initialize rust: %v", err)
50+
fmt.Printf("Failed to initialize golang LSP client: %v", err)
5751
os.Exit(1)
5852
}
59-
}()
60-
go func() {
61-
defer wg.Done()
62-
rustLSP, err = NewLSPClient("/root/codes/abcoder/testdata/rust2", "/root/codes/abcoder/testdata/rust2/Cargo.toml", time.Second*15, ClientOptions{
63-
Server: "rust-analyzer",
64-
Language: "rust",
65-
Verbose: true,
66-
})
67-
if err != nil {
68-
fmt.Printf("Failed to initialize rust LSP client: %v", err)
69-
}
70-
}()
71-
wg.Wait()
7253

73-
c := m.Run()
54+
wg := sync.WaitGroup{}
55+
wg.Add(1)
56+
// go func() {
57+
// defer wg.Done()
58+
// rustLSP, err = NewLSPClient("/root/codes/abcoder/tmp/lust-example-item", "/root/codes/abcoder/tmp/lust-example-item/src/lib.rs", time.Second*30, ClientOptions{
59+
// Server: "rust-analyzer",
60+
// Language: "rust",
61+
// Verbose: true,
62+
// })
63+
// if err != nil {
64+
// fmt.Printf("Failed to initialize rust: %v", err)
65+
// os.Exit(1)
66+
// }
67+
// }()
68+
go func() {
69+
defer wg.Done()
70+
rustLSP, err = NewLSPClient(rootDir+"/rust2", rootDir+"/rust2/Cargo.toml", time.Second*15, ClientOptions{
71+
Server: "rust-analyzer",
72+
Language: "rust",
73+
Verbose: true,
74+
})
75+
if err != nil {
76+
fmt.Printf("Failed to initialize rust LSP client: %v", err)
77+
}
78+
}()
79+
wg.Wait()
7480

75-
golangLSP.Close()
76-
rustLSP.Close()
77-
os.Exit(c)
81+
// c := m.Run()
82+
83+
// golangLSP.Close()
84+
// rustLSP.Close()
85+
// os.Exit(c)
86+
87+
})()
7888
}
7989

8090
func TestGolang(t *testing.T) {
91+
testClientInit(t)
92+
8193
uri := NewURI(rootDir + "/golang/pkg/entity/entity.go")
8294

8395
// documentSymbol
@@ -87,11 +99,11 @@ func TestGolang(t *testing.T) {
8799
t.Fatalf("Document Symbol failed: %v", err)
88100
}
89101
fmt.Printf("Document Symbol: %#v\n", symbols)
90-
// js, err := json.Marshal(symbols)
91-
// if err != nil {
92-
// t.Fatalf("Marshal Document Symbol failed: %v", err)
93-
// }
94-
// os.WriteFile("./symbol_golang.json", js, 0644)
102+
js, err := json.Marshal(symbols)
103+
if err != nil {
104+
t.Fatalf("Marshal Document Symbol failed: %v", err)
105+
}
106+
os.WriteFile("./symbol_golang.json", js, 0644)
95107
})
96108

97109
// references
@@ -142,6 +154,8 @@ func TestGolang(t *testing.T) {
142154
}
143155

144156
func TestRust(t *testing.T) {
157+
testClientInit(t)
158+
145159
// url encode
146160
uri := NewURI(rootDir + "/rust2/src/entity/mod.rs")
147161

@@ -227,6 +241,8 @@ func TestRust(t *testing.T) {
227241
}
228242

229243
func TestSearchSymbol(t *testing.T) {
244+
testClientInit(t)
245+
230246
syms, err := rustLSP.DocumentSymbols(context.Background(), NewURI("/root/codes/abcoder/tmp/lust-example-item/src/lib.rs"))
231247
if err != nil {
232248
t.Fatalf("Document Symbol failed: %v", err)
@@ -240,6 +256,8 @@ func TestSearchSymbol(t *testing.T) {
240256
}
241257

242258
func TestFileStructure(t *testing.T) {
259+
testClientInit(t)
260+
243261
symbols, err := rustLSP.FileStructure(context.Background(), NewURI("/root/codes/abcoder/tmp/lust-example-item/target/debug/build/lust-gen-e0683cdee43abe70/out/lust_gen.rs"))
244262
if err != nil {
245263
t.Fatalf("File Structure failed: %v", err)

src/lang/lsp/lsp.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// Copyright 2025 CloudWeGo Authors
2-
//
2+
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
55
// You may obtain a copy of the License at
6-
//
6+
//
77
// https://www.apache.org/licenses/LICENSE-2.0
8-
//
8+
//
99
// Unless required by applicable law or agreed to in writing, software
1010
// distributed under the License is distributed on an "AS IS" BASIS,
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -429,6 +429,10 @@ func (cli *LSPClient) LineCounts(uri DocumentURI) []int {
429429
return f.LineCounts
430430
}
431431

432+
func (cli *LSPClient) GetFile(uri DocumentURI) *TextDocumentItem {
433+
return cli.files[uri]
434+
}
435+
432436
func (cli *LSPClient) GetParent(sym *DocumentSymbol) (ret *DocumentSymbol) {
433437
if sym == nil {
434438
return nil

src/lang/lsp/utils.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// Copyright 2025 CloudWeGo Authors
2-
//
2+
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
55
// You may obtain a copy of the License at
6-
//
6+
//
77
// https://www.apache.org/licenses/LICENSE-2.0
8-
//
8+
//
99
// Unless required by applicable law or agreed to in writing, software
1010
// distributed under the License is distributed on an "AS IS" BASIS,
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,7 +30,10 @@
3030

3131
package lsp
3232

33-
import "github.com/cloudwego/abcoder/src/lang/utils"
33+
import (
34+
"github.com/cloudwego/abcoder/src/lang/log"
35+
"github.com/cloudwego/abcoder/src/lang/utils"
36+
)
3437

3538
func GetDistance(text string, start Position, pos Position) int {
3639
lines := utils.CountLinesCached(text)
@@ -47,3 +50,22 @@ func ChunkHead(text string, textPos Position, pos Position) string {
4750
}
4851
return text[:distance]
4952
}
53+
54+
// calculate the relative index of a position to a text
55+
func RelativePostionWithLines(lines []int, textPos Position, pos Position) int {
56+
// find the line of the position
57+
l := pos.Line - textPos.Line
58+
59+
return lines[l] + pos.Character - textPos.Character
60+
}
61+
62+
func PositionOffset(text string, pos Position) int {
63+
if pos.Line < 1 || pos.Character < 1 {
64+
log.Error("invalid text position: %+v", pos)
65+
return -1
66+
}
67+
lines := utils.CountLinesCached(text)
68+
defer utils.PutCount(lines)
69+
70+
return RelativePostionWithLines(*lines, Position{Line: 1, Character: 1}, pos)
71+
}

0 commit comments

Comments
 (0)