all repos

clerk @ 5581d44cd1b540070af709dfb8aafc68722f1195

missing tooling for ledger/hledger

clerk/internal/lsp/textdocument_references.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
lsp: workspace/symbol and textdocument/references, 1 month ago
1
package lsp
2
3
import (
4
	"context"
5
6
	"go.lsp.dev/protocol"
7
8
	"olexsmir.xyz/clerk/internal/analyzer"
9
)
10
11
func (s *server) References(_ context.Context, params *protocol.ReferenceParams) ([]protocol.Location, error) {
12
	state, ok := s.getDocState(params.TextDocument.URI)
13
	if !ok {
14
		return nil, nil
15
	}
16
17
	an := s.analysisFor(params.TextDocument.URI)
18
	if an == nil {
19
		return nil, nil
20
	}
21
	cursor := state.lineIdx.Offset(int(params.Position.Line), int(params.Position.Character))
22
	ref := findSymbolUnderCursor(an, params.TextDocument.URI.Path(), state.text, cursor)
23
	if ref == nil {
24
		return nil, nil
25
	}
26
	return findReferences(an, ref, params.Context.IncludeDeclaration), nil
27
}
28
29
func findReferences(an *analyzer.Analysis, ref *symbolRef, includeDeclaration bool) []protocol.Location {
30
	switch ref.kind {
31
	case symbolAccount:
32
		return findAccountReferences(an, ref.name, includeDeclaration)
33
	case symbolCommodity:
34
		return findCommodityReferences(an, ref.name, includeDeclaration)
35
	case symbolPayee:
36
		return findPayeeReferences(an, ref.name, includeDeclaration)
37
	case symbolTag:
38
		return findTagReferences(an, ref.name, includeDeclaration)
39
	}
40
	return nil
41
}
42
43
func findAccountReferences(an *analyzer.Analysis, name string, includeDeclaration bool) []protocol.Location {
44
	var locations []protocol.Location
45
	for _, candidate := range an.AccountNames {
46
		if !accountMatches(candidate, name) {
47
			continue
48
		}
49
		info := an.Accounts[candidate]
50
		for _, u := range info.Usages {
51
			appendLocation(&locations, locationFor(an, u.FileIndex, u.Posting.Account.Span))
52
		}
53
		if includeDeclaration {
54
			for _, d := range info.Directives {
55
				appendLocation(&locations, locationForDirective(an, d, d.Account.Span))
56
			}
57
		}
58
	}
59
	return locations
60
}
61
62
func findCommodityReferences(an *analyzer.Analysis, symbol string, includeDeclaration bool) []protocol.Location {
63
	info := an.Commodities[symbol]
64
	if info == nil {
65
		return nil
66
	}
67
	var locations []protocol.Location
68
	for _, u := range info.Usages {
69
		appendLocation(&locations, locationFor(an, u.FileIndex, u.Amount.CommoditySpan))
70
	}
71
	if includeDeclaration {
72
		for _, d := range info.Directives {
73
			appendLocation(&locations, locationForDirective(an, d, d.CommoditySpan))
74
		}
75
	}
76
	return locations
77
}
78
79
func findPayeeReferences(an *analyzer.Analysis, name string, includeDeclaration bool) []protocol.Location {
80
	info := an.Payees[name]
81
	if info == nil {
82
		return nil
83
	}
84
	var locations []protocol.Location
85
	for _, u := range info.Usage {
86
		appendLocation(&locations, locationFor(an, u.FileIndex, u.Payee.Span))
87
	}
88
	if includeDeclaration {
89
		for _, d := range info.Directives {
90
			if d.Name != nil {
91
				appendLocation(&locations, locationForDirective(an, d, d.Name.Span))
92
			}
93
		}
94
	}
95
	return locations
96
}
97
98
func findTagReferences(an *analyzer.Analysis, key string, includeDeclaration bool) []protocol.Location {
99
	info := an.Tags[key]
100
	if info == nil {
101
		return nil
102
	}
103
	var locations []protocol.Location
104
	for _, u := range info.Usage {
105
		span := tagKeySpan(string(an.Files[u.FileIndex].Src), u.Tag)
106
		appendLocation(&locations, locationFor(an, u.FileIndex, span))
107
	}
108
	if includeDeclaration {
109
		for _, d := range info.Directives {
110
			fileIdx := fileIndexForEntry(an, d)
111
			if fileIdx < 0 {
112
				continue
113
			}
114
			if span, ok := tagDirectiveSpan(string(an.Files[fileIdx].Src), d); ok {
115
				appendLocation(&locations, locationFor(an, fileIdx, span))
116
			}
117
		}
118
	}
119
	return locations
120
}
121
122
func appendLocation(locations *[]protocol.Location, loc *protocol.Location) {
123
	if loc != nil {
124
		*locations = append(*locations, *loc)
125
	}
126
}