all repos

clerk @ f9e2eff

missing tooling for ledger/hledger

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
lsp: rename, 1 month 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
	diagCancel context.CancelFunc
34
35
	cfgMu  sync.RWMutex
36
	config Config
37
}
38
39
func (s *server) analysis() *analyzer.Analysis {
40
	s.mu.Lock()
41
	a := s.current
42
	s.mu.Unlock()
43
	if a != nil {
44
		return a
45
	}
46
	return s.buildAnalysis()
47
}
48
49
func (s *server) buildAnalysis() *analyzer.Analysis {
50
	s.mu.Lock()
51
	docs := make(map[uri.URI]docState, len(s.openDocs))
52
	maps.Copy(docs, s.openDocs)
53
	s.mu.Unlock()
54
55
	var a *analyzer.Analysis
56
	for duri, state := range docs {
57
		rj := s.loader.ResolveBytes(duri.Path(), []byte(state.text))
58
		if a == nil {
59
			a = analyzer.Build(rj)
60
		}
61
	}
62
	return a
63
}
64
65
func (s *server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
66
	s.applySettings(params.InitializationOptions)
67
	return &protocol.InitializeResult{
68
		ServerInfo: protocol.ServerInfo{
69
			Name:    s.name,
70
			Version: protocol.NewOptional(s.version),
71
		},
72
		Capabilities: protocol.ServerCapabilities{
73
			DocumentFormattingProvider: &protocol.DocumentFormattingOptions{},
74
			DefinitionProvider:         protocol.Boolean(true),
75
			RenameProvider: &protocol.RenameOptions{
76
				PrepareProvider: new(true),
77
			},
78
			CompletionProvider: &protocol.CompletionOptions{
79
				TriggerCharacters: []string{":", "@"},
80
			},
81
			TextDocumentSync: &protocol.TextDocumentSyncOptions{
82
				OpenClose: new(true),
83
				Change:    new(protocol.TextDocumentSyncKindFull),
84
			},
85
			SemanticTokensProvider: &protocol.SemanticTokensOptions{
86
				Legend: getSemanticTokensLegend(),
87
				Range:  protocol.Boolean(true),
88
				Full:   protocol.Boolean(true),
89
			},
90
		},
91
	}, nil
92
}
93
94
func (s *server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
95
	s.scheduleDiagnostics(ctx)
96
	return nil
97
}
98
99
func (s *server) DidChangeConfiguration(ctx context.Context, params *protocol.DidChangeConfigurationParams) error {
100
	s.applySettings(params.Settings)
101
	return nil
102
}
103
104
func (s *server) Shutdown(ctx context.Context) error {
105
	return nil
106
}
107
108
func (s *server) Exit(ctx context.Context) error {
109
	return nil
110
}
111
112
func (s *server) applySettings(v protocol.LSPAny) {
113
	s.cfgMu.Lock()
114
	if err := s.config.merge(v); err != nil {
115
		s.log.Error("failed to merge config", "err", err)
116
	}
117
	s.cfgMu.Unlock()
118
}