turbo-editors/turbo-corepublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

symbols.go · 111 lines · 3.5 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 11h ago1// Finding a symbol by name: in the file in front, or anywhere in the project.
2
3package app
4
5import (
6 "context"
7 "errors"
8 "fmt"
9 "path/filepath"
10 "strings"
11
📦 Turbo Core f3ade8d k33g 3h ago12 "rickub.com/turbo-editors/turbo-core/lsp"
13 "rickub.com/turbo-editors/turbo-core/ui"
🛟 Updated. 28d5985 k33g 11h ago14)
15
16// SymbolInFile lists what the file in front declares, and goes to the one
17// chosen. It is the outline of a file, offered as a way of moving around it.
18func (a *App) SymbolInFile() {
19 view := a.activeView()
20 if view == nil {
21 return
22 }
23
24 symbols, err := a.language.DocumentSymbols(context.Background(), view.Buffer().Path())
25 a.showSymbols("Symbols in file", symbols, err, outlineLabel)
26}
27
28// SymbolInProject asks for a name and lists the symbols matching it anywhere
29// in the project.
30//
31// The query goes to the server, which decides what "matching" means — gopls
32// matches fuzzily, others by prefix. Deciding that here would mean filtering
33// an answer the server has already filtered, by a different rule.
34func (a *App) SymbolInProject() {
35 dialog, field := NewPromptDialog("Symbol in project", "Name:", "", a.screenRect())
36 a.pushModal(dialog, func(result ui.Result) {
37 if result != ui.ResultOK {
38 return
39 }
40 query := strings.TrimSpace(field.Text())
41 if query == "" {
42 // An empty query is answered by some servers with the whole
43 // project and by others with nothing. Neither is what was meant.
44 a.Message("Nothing to look for")
45 return
46 }
47
48 symbols, err := a.language.WorkspaceSymbols(context.Background(), query)
49 a.showSymbols("Symbols matching "+query, symbols, err, projectLabel)
50 })
51}
52
53// showSymbols offers a list of symbols and goes to the one chosen.
54//
55// Unlike a list of locations, a single symbol still gets the list. Finding one
56// match for a name is an answer worth reading — it says *which* thing has that
57// name and where — where a single definition is somewhere you simply wanted to
58// be taken.
59func (a *App) showSymbols(title string, symbols []lsp.Symbol, err error, label func(lsp.Symbol) string) {
60 switch {
61 case errors.Is(err, lsp.ErrNotReady):
62 a.Message(a.language.Status())
63 return
64 case err != nil:
65 a.Message("Symbols: " + err.Error())
66 return
67 case len(symbols) == 0:
68 a.Message("No symbols found")
69 return
70 }
71
72 labels := make([]string, 0, len(symbols))
73 for _, symbol := range symbols {
74 labels = append(labels, label(symbol))
75 }
76
77 dialog, list := NewChoiceDialog(fmt.Sprintf("%s (%d)", title, len(symbols)), labels, 0, a.screenRect())
78 a.pushModal(dialog, func(result ui.Result) {
79 if result == ui.ResultOK && list.Selected() >= 0 {
80 a.jumpTo(symbols[list.Selected()].Location)
81 }
82 })
83}
84
85// outlineLabel is how a symbol reads in a file's own outline: indented by how
86// deeply it is nested, so a type's methods sit under it, with the kind after
87// the name rather than before it so the names line up and can be read down.
88func outlineLabel(symbol lsp.Symbol) string {
89 label := strings.Repeat(" ", symbol.Depth) + symbol.Name
90 if kind := symbol.Kind.String(); kind != "" {
91 label += " " + kind
92 }
93 return label
94}
95
96// projectLabel is how a symbol reads in a search across the project: the name,
97// what contains it, and which file — because the file is what tells two
98// identically-named symbols apart, and there are usually several.
99func projectLabel(symbol lsp.Symbol) string {
100 label := symbol.Name
101 if kind := symbol.Kind.String(); kind != "" {
102 label += " " + kind
103 }
104 if symbol.Container != "" {
105 label += " in " + symbol.Container
106 }
107 if path := lsp.URIToPath(symbol.Location.URI); path != "" {
108 label += " (" + filepath.Base(path) + ")"
109 }
110 return label
111}