all repos

clerk @ 8e87a98

missing tooling for ledger/hledger

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
lsp: format, 20 days ago
1
package lsp
2
3
import (
4
	"context"
5
	"fmt"
6
	"strings"
7
8
	"go.lsp.dev/protocol"
9
	"olexsmir.xyz/clerk/journal/lexer"
10
	"olexsmir.xyz/clerk/journal/parser"
11
)
12
13
func (s *server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
14
	s.mu.RLock()
15
	state, ok := s.docs[params.TextDocument.URI]
16
	s.mu.RUnlock()
17
	if !ok {
18
		return nil, nil
19
	}
20
21
	lex := lexer.New(params.TextDocument.URI.Path(), []byte(state.text))
22
	jnrl := parser.New(lex).ParseJournal()
23
	if len(jnrl.Errors) > 0 {
24
		return nil, fmt.Errorf("can't format file with errors: %v", jnrl.Errors[0].Message)
25
	}
26
27
	var buf strings.Builder
28
	if err := s.printerCfg.Fprint(&buf, jnrl); err != nil {
29
		return nil, fmt.Errorf("format: %w", err)
30
	}
31
	newText := buf.String()
32
33
	if newText == state.text {
34
		return nil, nil
35
	}
36
37
	endLine, endChar := getEndLineAndChar(state.text)
38
	return []protocol.TextEdit{{
39
		NewText: newText,
40
		Range: protocol.Range{
41
			Start: protocol.Position{
42
				Line:      0,
43
				Character: 0,
44
			},
45
			End: protocol.Position{
46
				Line:      uint32(endLine),
47
				Character: uint32(endChar),
48
			},
49
		},
50
	}}, nil
51
}
52
53
func getEndLineAndChar(txt string) (int, int) {
54
	endLine := 0
55
	lastNewline := -1
56
	for i, ch := range txt {
57
		if ch == '\n' {
58
			endLine++
59
			lastNewline = i
60
		}
61
	}
62
	endChar := 0
63
	if lastNewline >= 0 {
64
		if lastNewline == len(txt)-1 {
65
			endLine++
66
			endChar = 0
67
		} else {
68
			endChar = len(txt) - lastNewline - 1
69
		}
70
	} else {
71
		endChar = len(txt)
72
	}
73
	return endLine, endChar
74
}