all repos

clerk @ d7328be

missing tooling for ledger/hledger

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
lsp: completion, 2 months ago
1
package lsp
2
3
import (
4
	"context"
5
	"log/slog"
6
	"maps"
7
	"sync"
8
9
	"go.lsp.dev/protocol"
10
	"go.lsp.dev/uri"
11
12
	"olexsmir.xyz/clerk/internal/analyzer"
13
	"olexsmir.xyz/clerk/internal/linter"
14
	"olexsmir.xyz/clerk/journal"
15
	"olexsmir.xyz/clerk/journal/printer"
16
)
17
18
type server struct {
19
	protocol.UnimplementedServer
20
21
	client protocol.Client
22
	log    *slog.Logger
23
24
	version, name string
25
26
	linter  *linter.Linter
27
	loader  *journal.Loader
28
	printer *printer.Config
29
30
	mu       sync.Mutex
31
	openDocs map[uri.URI]docState
32
	current  *analyzer.Analysis
33
34
	cfgMu  sync.RWMutex
35
	config Config
36
37
	diagMu     sync.Mutex
38
	diagCancel context.CancelFunc
39
}
40
41
func (s *server) analysis() *analyzer.Analysis {
42
	s.mu.Lock()
43
	a := s.current
44
	s.mu.Unlock()
45
	if a != nil {
46
		return a
47
	}
48
	return s.buildAnalysis()
49
}
50
51
func (s *server) buildAnalysis() *analyzer.Analysis {
52
	s.mu.Lock()
53
	docs := make(map[uri.URI]docState, len(s.openDocs))
54
	maps.Copy(docs, s.openDocs)
55
	s.mu.Unlock()
56
57
	var a *analyzer.Analysis
58
	for duri, state := range docs {
59
		rj := s.loader.ResolveBytes(duri.Path(), []byte(state.text))
60
		if a == nil {
61
			a = analyzer.Build(rj)
62
		}
63
	}
64
	return a
65
}
66
67
type docState struct {
68
	text       string
69
	version    int32
70
	languageID protocol.LanguageKind
71
	semTokens  []semanticToken // cached semantic tokens
72
}
73
74
func (s *server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
75
	s.applySettings(params.InitializationOptions)
76
	return &protocol.InitializeResult{
77
		ServerInfo: protocol.ServerInfo{
78
			Name:    s.name,
79
			Version: protocol.NewOptional(s.version),
80
		},
81
		Capabilities: protocol.ServerCapabilities{
82
			DocumentFormattingProvider: &protocol.DocumentFormattingOptions{},
83
			CompletionProvider: &protocol.CompletionOptions{
84
				TriggerCharacters: []string{":", "@"},
85
			},
86
			TextDocumentSync: &protocol.TextDocumentSyncOptions{
87
				OpenClose: new(true),
88
				Change:    new(protocol.TextDocumentSyncKindFull),
89
			},
90
			SemanticTokensProvider: &protocol.SemanticTokensOptions{
91
				Legend: getSemanticTokensLegend(),
92
				Range:  protocol.Boolean(true),
93
				Full:   protocol.Boolean(true),
94
			},
95
		},
96
	}, nil
97
}
98
99
func (s *server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
100
	s.scheduleDiagnostics(ctx)
101
	return nil
102
}
103
104
func (s *server) DidChangeConfiguration(ctx context.Context, params *protocol.DidChangeConfigurationParams) error {
105
	s.applySettings(params.Settings)
106
	return nil
107
}
108
109
func (s *server) Shutdown(ctx context.Context) error {
110
	return nil
111
}
112
113
func (s *server) Exit(ctx context.Context) error {
114
	return nil
115
}
116
117
func (s *server) applySettings(v protocol.LSPAny) {
118
	s.cfgMu.Lock()
119
	s.config.merge(v)
120
	s.cfgMu.Unlock()
121
}