feat(lsp): add workspace symbol search

Assisted-by: MiMo V2.5 Pro
This commit is contained in:
2026-08-21 01:20:35 +02:00
parent f5fcaf9fa6
commit e4c9d78968
4 changed files with 89 additions and 0 deletions
+44
View File
@@ -452,6 +452,50 @@ func (s *Server) documentHighlights(p documentHighlightParams) []DocumentHighlig
return out
}
// workspaceSymbols searches all open documents for symbols matching the query.
func (s *Server) workspaceSymbols(p workspaceSymbolParams) []WorkspaceSymbol {
if p.Query == "" {
return nil
}
query := strings.ToLower(p.Query)
var out []WorkspaceSymbol
for uri, text := range s.docs {
f, _ := parser.Parse(uriPath(uri), text)
if f == nil {
continue
}
for _, d := range f.Decls {
switch dd := d.(type) {
case *ast.Text:
if strings.Contains(strings.ToLower(dd.Name.Name), query) {
out = append(out, WorkspaceSymbol{
Name: dd.Name.Name,
Kind: symFunction,
Location: Location{URI: uri, Range: symRange(dd.Name)},
})
}
case *ast.Globl:
if dd.Name != nil && strings.Contains(strings.ToLower(dd.Name.Name), query) {
out = append(out, WorkspaceSymbol{
Name: dd.Name.Name,
Kind: symConstant,
Location: Location{URI: uri, Range: symRange(dd.Name)},
})
}
case *ast.Data:
if dd.Name != nil && strings.Contains(strings.ToLower(dd.Name.Name), query) {
out = append(out, WorkspaceSymbol{
Name: dd.Name.Name,
Kind: symConstant,
Location: Location{URI: uri, Range: symRange(dd.Name)},
})
}
}
}
}
return out
}
// documentSymbols returns functions and their labels, plus global symbols.
func (s *Server) documentSymbols(p documentSymbolParams) []DocumentSymbol {
text := s.docs[p.TextDocument.URI]
+14
View File
@@ -312,6 +312,7 @@ type ServerCapabilities struct {
DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"`
CodeActionProvider bool `json:"codeActionProvider,omitempty"`
SignatureHelpProvider map[string]any `json:"signatureHelpProvider,omitempty"`
WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"`
SemanticTokensProvider map[string]any `json:"semanticTokensProvider,omitempty"`
InlayHintProvider bool `json:"inlayHintProvider,omitempty"`
DiagnosticProvider map[string]any `json:"diagnosticProvider,omitempty"`
@@ -378,6 +379,19 @@ const (
highlightWrite = 2
)
// --- workspace symbols -------------------------------------------------------
type workspaceSymbolParams struct {
Query string `json:"query"`
}
// WorkspaceSymbol represents a symbol found across the workspace.
type WorkspaceSymbol struct {
Name string `json:"name"`
Kind int `json:"kind"`
Location Location `json:"location"`
}
type initializeResult struct {
Capabilities ServerCapabilities `json:"capabilities"`
ServerInfo map[string]string `json:"serverInfo,omitempty"`
+6
View File
@@ -135,6 +135,7 @@ func (s *Server) dispatch(msg *rpcMessage) (exit bool) {
DocumentHighlightProvider: true,
CodeActionProvider: true,
SignatureHelpProvider: map[string]any{"triggerCharacters": []string{"("}},
WorkspaceSymbolProvider: true,
},
ServerInfo: map[string]string{"name": "gasm", "version": "0.31.1"},
})
@@ -228,6 +229,11 @@ func (s *Server) dispatch(msg *rpcMessage) (exit bool) {
json.Unmarshal(msg.Params, &p)
s.respond(msg.ID, s.semanticTokens(p))
case "workspace/symbol":
var p workspaceSymbolParams
json.Unmarshal(msg.Params, &p)
s.respond(msg.ID, s.workspaceSymbols(p))
case "shutdown":
s.respond(msg.ID, nil)
+25
View File
@@ -450,6 +450,31 @@ func TestDocumentHighlights(t *testing.T) {
}
}
func TestWorkspaceSymbols(t *testing.T) {
in := session("file:///f_amd64.s", cleanDoc) +
frame(13, "workspace/symbol", map[string]any{
"query": "foo",
}) + frame(nil, "exit", nil)
msgs := run(t, in)
resp := findByID(msgs, 13)
if resp == nil {
t.Fatal("no workspace/symbol response")
}
var syms []WorkspaceSymbol
if err := json.Unmarshal(mustResult(t, resp), &syms); err != nil {
t.Fatal(err)
}
found := false
for _, s := range syms {
if s.Name == "foo" {
found = true
}
}
if !found {
t.Fatalf("want symbol foo in results, got %+v", syms)
}
}
func TestMethodNotFound(t *testing.T) {
in := frame(9, "bogus/method", map[string]any{}) + frame(nil, "exit", nil)
msgs := run(t, in)