all repos

clerk @ 2e3f494

missing tooling for ledger/hledger

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

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