From 3d01ffcc0e8782feb7eb80b8d4a963bfa7bf053b Mon Sep 17 00:00:00 2001 From: Hoblovski Date: Thu, 30 Oct 2025 16:13:07 +0800 Subject: [PATCH 1/6] fix: make hierarchical docsyms right --- lang/lsp/client.go | 6 ++++-- lang/lsp/lsp.go | 9 +++++++++ lang/lsp/lsp_methods.go | 28 ++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lang/lsp/client.go b/lang/lsp/client.go index 4afe4ad4..3b53df13 100644 --- a/lang/lsp/client.go +++ b/lang/lsp/client.go @@ -141,8 +141,10 @@ func initLSPClient(ctx context.Context, svr io.ReadWriteCloser, dir DocumentURI, "dynamicRegistration": true, }, }, - "documentSymbol": map[string]interface{}{ - "hierarchicalDocumentSymbolSupport": true, + "textDocument": map[string]interface{}{ + "documentSymbol": map[string]interface{}{ + "hierarchicalDocumentSymbolSupport": true, + }, }, } diff --git a/lang/lsp/lsp.go b/lang/lsp/lsp.go index 39c6286d..12e03a5d 100644 --- a/lang/lsp/lsp.go +++ b/lang/lsp/lsp.go @@ -180,6 +180,15 @@ type TextDocumentItem struct { Definitions map[Position][]Location `json:"-"` } +type HierarchicalDocumentSymbol struct { + Name string `json:"name"` + Detail string `json:"detail,omitempty"` + Kind SymbolKind `json:"kind"` + Range Range `json:"range"` + SelectionRange Range `json:"selectionRange"` + Children []HierarchicalDocumentSymbol `json:"children,omitempty"` +} + type DocumentSymbol struct { Name string `json:"name"` Kind SymbolKind `json:"kind"` diff --git a/lang/lsp/lsp_methods.go b/lang/lsp/lsp_methods.go index 99b3e0a0..2350461d 100644 --- a/lang/lsp/lsp_methods.go +++ b/lang/lsp/lsp_methods.go @@ -16,6 +16,7 @@ package lsp import ( "context" + "encoding/json" "fmt" "math" "os" @@ -63,6 +64,25 @@ func (cli *LSPClient) DidOpen(ctx context.Context, file DocumentURI) (*TextDocum return f, nil } +func flattenHierarchicalDocumentSymbols(symbols []HierarchicalDocumentSymbol, uri DocumentURI) []DocumentSymbol { + var result []DocumentSymbol + for _, sym := range symbols { + result = append(result, DocumentSymbol{ + Name: sym.Name, + Kind: sym.Kind, + Location: Location{ + URI: uri, + Range: sym.Range, + }, + }) + if len(sym.Children) > 0 { + childSymbols := flattenHierarchicalDocumentSymbols(sym.Children, uri) + result = append(result, childSymbols...) + } + } + return result +} + func (cli *LSPClient) DocumentSymbols(ctx context.Context, file DocumentURI) (map[Range]*DocumentSymbol, error) { // open file first f, err := cli.DidOpen(ctx, file) @@ -78,10 +98,14 @@ func (cli *LSPClient) DocumentSymbols(ctx context.Context, file DocumentURI) (ma URI: uri, }, } - var resp []DocumentSymbol - if err := cli.Call(ctx, "textDocument/documentSymbol", req, &resp); err != nil { + var respHierarchical []HierarchicalDocumentSymbol + if err := cli.Call(ctx, "textDocument/documentSymbol", req, &respHierarchical); err != nil { return nil, err } + if s, err := json.MarshalIndent(respHierarchical, "", " "); err == nil { + _ = os.WriteFile("docsyms_hie.json", s, 0644) + } + resp := flattenHierarchicalDocumentSymbols(respHierarchical, file) // cache symbols f.Symbols = make(map[Range]*DocumentSymbol, len(resp)) for i := range resp { From ceeda479af65d47bd9f56b18623d77a5efab0de3 Mon Sep 17 00:00:00 2001 From: Hoblovski Date: Thu, 30 Oct 2025 16:41:45 +0800 Subject: [PATCH 2/6] fix: installation output --- lang/python/lib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/python/lib.go b/lang/python/lib.go index 5dbe5dbf..34415562 100644 --- a/lang/python/lib.go +++ b/lang/python/lib.go @@ -59,7 +59,7 @@ func CheckPythonVersion() error { func InstallLanguageServer() (string, error) { if out, err := exec.Command("pylsp", "--version").CombinedOutput(); err == nil { - log.Info("pylsp already installed: %v", out) + log.Info("pylsp already installed: %v", string(out)) return lspName, nil } if err := CheckPythonVersion(); err != nil { From 5d87108257e86dc29c31fd1c56d3e24fffbbfb1c Mon Sep 17 00:00:00 2001 From: Hoblovski Date: Thu, 30 Oct 2025 17:24:25 +0800 Subject: [PATCH 3/6] fix: compatibility with legacy LSPs who return SymbolInformation --- lang/lsp/lsp.go | 17 ++++++------- lang/lsp/lsp_methods.go | 55 ++++++++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/lang/lsp/lsp.go b/lang/lsp/lsp.go index 12e03a5d..92c7b62e 100644 --- a/lang/lsp/lsp.go +++ b/lang/lsp/lsp.go @@ -180,25 +180,22 @@ type TextDocumentItem struct { Definitions map[Position][]Location `json:"-"` } -type HierarchicalDocumentSymbol struct { - Name string `json:"name"` - Detail string `json:"detail,omitempty"` - Kind SymbolKind `json:"kind"` - Range Range `json:"range"` - SelectionRange Range `json:"selectionRange"` - Children []HierarchicalDocumentSymbol `json:"children,omitempty"` -} - type DocumentSymbol struct { Name string `json:"name"` Kind SymbolKind `json:"kind"` Tags []json.RawMessage `json:"tags"` - Location Location `json:"location"` Children []*DocumentSymbol `json:"children"` Text string `json:"text"` Tokens []Token `json:"tokens"` Node *sitter.Node `json:"-"` Role SymbolRole `json:"-"` + + // Older LSPs might return SymbolInformation[] which have `Location`. + // Newer LSPs return DocumentSymbol[] which have `Range` and `SelectionRange`. + // ABCoder uses `Location`, and converts `Range` to `Location` when needed. + Location Location `json:"location"` + Range *Range `json:"range"` + SelectionRange *Range `json:"selectionRange"` } type TextDocumentPositionParams struct { diff --git a/lang/lsp/lsp_methods.go b/lang/lsp/lsp_methods.go index 2350461d..e2d94796 100644 --- a/lang/lsp/lsp_methods.go +++ b/lang/lsp/lsp_methods.go @@ -16,7 +16,6 @@ package lsp import ( "context" - "encoding/json" "fmt" "math" "os" @@ -64,19 +63,38 @@ func (cli *LSPClient) DidOpen(ctx context.Context, file DocumentURI) (*TextDocum return f, nil } -func flattenHierarchicalDocumentSymbols(symbols []HierarchicalDocumentSymbol, uri DocumentURI) []DocumentSymbol { - var result []DocumentSymbol +func flattenDocumentSymbols(symbols []*DocumentSymbol, uri DocumentURI) []*DocumentSymbol { + var result []*DocumentSymbol for _, sym := range symbols { - result = append(result, DocumentSymbol{ - Name: sym.Name, - Kind: sym.Kind, - Location: Location{ + var location Location + if sym.Range != nil { + location = Location{ URI: uri, - Range: sym.Range, - }, - }) + Range: *sym.Range, + } + } else { + location = sym.Location + } + flatSymbol := DocumentSymbol{ + // copy + Name: sym.Name, + Kind: sym.Kind, + Tags: sym.Tags, + Text: sym.Text, + Tokens: sym.Tokens, + Node: sym.Node, + Children: sym.Children, + // new + Location: location, + // empty + Role: 0, + Range: nil, + SelectionRange: nil, + } + result = append(result, &flatSymbol) + if len(sym.Children) > 0 { - childSymbols := flattenHierarchicalDocumentSymbols(sym.Children, uri) + childSymbols := flattenDocumentSymbols(sym.Children, uri) result = append(result, childSymbols...) } } @@ -98,18 +116,15 @@ func (cli *LSPClient) DocumentSymbols(ctx context.Context, file DocumentURI) (ma URI: uri, }, } - var respHierarchical []HierarchicalDocumentSymbol - if err := cli.Call(ctx, "textDocument/documentSymbol", req, &respHierarchical); err != nil { + var resp []*DocumentSymbol + if err := cli.Call(ctx, "textDocument/documentSymbol", req, &resp); err != nil { return nil, err } - if s, err := json.MarshalIndent(respHierarchical, "", " "); err == nil { - _ = os.WriteFile("docsyms_hie.json", s, 0644) - } - resp := flattenHierarchicalDocumentSymbols(respHierarchical, file) + respFlatten := flattenDocumentSymbols(resp, file) // cache symbols - f.Symbols = make(map[Range]*DocumentSymbol, len(resp)) - for i := range resp { - s := &resp[i] + f.Symbols = make(map[Range]*DocumentSymbol, len(respFlatten)) + for i := range respFlatten { + s := respFlatten[i] f.Symbols[s.Location.Range] = s } return f.Symbols, nil From fdfd874d34bf1a96c5c4a8279131fe85125f6438 Mon Sep 17 00:00:00 2001 From: Hoblovski Date: Thu, 30 Oct 2025 17:54:51 +0800 Subject: [PATCH 4/6] fix: java wont need nested symbols --- lang/lsp/client.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lang/lsp/client.go b/lang/lsp/client.go index 3b53df13..8cf8b321 100644 --- a/lang/lsp/client.go +++ b/lang/lsp/client.go @@ -56,7 +56,7 @@ func NewLSPClient(repo string, openfile string, wait time.Duration, opts ClientO return nil, err } - cli, err := initLSPClient(context.Background(), svr, NewURI(repo), opts.Verbose, opts.InitializationOptions) + cli, err := initLSPClient(context.Background(), svr, NewURI(repo), opts.Verbose, opts.Language, opts.InitializationOptions) if err != nil { return nil, err } @@ -122,7 +122,7 @@ func (c *LSPClient) InitFiles() { } } -func initLSPClient(ctx context.Context, svr io.ReadWriteCloser, dir DocumentURI, verbose bool, InitializationOptions interface{}) (*LSPClient, error) { +func initLSPClient(ctx context.Context, svr io.ReadWriteCloser, dir DocumentURI, verbose bool, language uniast.Language, InitializationOptions interface{}) (*LSPClient, error) { h := newLSPHandler() stream := jsonrpc2.NewBufferedStream(svr, jsonrpc2.VSCodeObjectCodec{}) conn := jsonrpc2.NewConn(ctx, stream, h) @@ -143,7 +143,7 @@ func initLSPClient(ctx context.Context, svr io.ReadWriteCloser, dir DocumentURI, }, "textDocument": map[string]interface{}{ "documentSymbol": map[string]interface{}{ - "hierarchicalDocumentSymbolSupport": true, + "hierarchicalDocumentSymbolSupport": (language != uniast.Java), }, }, } From 98911ef6025ef71620125f7b9c8ab9a3fa7b60a0 Mon Sep 17 00:00:00 2001 From: Hoblovski Date: Thu, 30 Oct 2025 18:06:34 +0800 Subject: [PATCH 5/6] fix: simply keep behavior of golang same as before --- lang/lsp/client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lang/lsp/client.go b/lang/lsp/client.go index 8cf8b321..f4e3767c 100644 --- a/lang/lsp/client.go +++ b/lang/lsp/client.go @@ -143,7 +143,9 @@ func initLSPClient(ctx context.Context, svr io.ReadWriteCloser, dir DocumentURI, }, "textDocument": map[string]interface{}{ "documentSymbol": map[string]interface{}{ - "hierarchicalDocumentSymbolSupport": (language != uniast.Java), + // Java uses tree-sitter instead of hierarchical symbols + // Golang stays the same as older versions. ABCoder do not use gopls, so don't play with it. + "hierarchicalDocumentSymbolSupport": (language != uniast.Java && language != uniast.Golang), }, }, } From 46a88b8510b13c1c548790a8bad260a88a4c99c9 Mon Sep 17 00:00:00 2001 From: Hoblovski Date: Fri, 31 Oct 2025 14:43:04 +0800 Subject: [PATCH 6/6] fix: ignore ToolVersion and skip Java --- .github/workflows/go-test.yml | 4 +++- .github/workflows/regression.yml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 5eda12b5..b7d9bbf6 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -21,8 +21,10 @@ jobs: # test tries to retrieve non-existent node # TestParser_NodeFieldsConsistency: # Vars.Content only includes name, not whole body + # TestCollector_CollectByTreeSitter_Java + # JDTLS download was rate-limited on github actions SKIPPED_TESTS: >- - TestPatcher|TestCases|Test_goParser_ParseNode|TestParser_NodeFieldsConsistency|TestRustLSP + TestPatcher|TestCases|Test_goParser_ParseNode|TestParser_NodeFieldsConsistency|TestRustLSP|TestCollector_CollectByTreeSitter_Java steps: - name: Checkout code diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 57095288..9d806318 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -18,6 +18,7 @@ jobs: DIFFJSON_IGNORE: > ['id'] ['Path'] + ['ToolVersion'] ['Modules']['a.b/c']['Dependencies']['a.b/c'] ['Modules']['a.b/c/cmdx']['Dependencies']['a.b/c/cmdx'] steps: