all repos

clerk @ e2e9e2b

missing tooling for ledger/hledger

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
lsp: add formatter, 19 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
	text, ok := s.getDocText(params.TextDocument.URI)
15
	if !ok {
16
		return nil, nil
17
	}
18
19
	lex := lexer.New(params.TextDocument.URI.Path(), []byte(text))
20
	jnrl := parser.New(lex).ParseJournal()
21
	if len(jnrl.Errors) > 0 {
22
		return nil, fmt.Errorf("can't format file with errors: %v", jnrl.Errors[0].Message) // TODO: report all errors
23
	}
24
25
	var buf strings.Builder
26
	if err := s.printer.Fprint(&buf, jnrl); err != nil {
27
		return nil, fmt.Errorf("format: %w", err)
28
	}
29
30
	newText := buf.String()
31
	if newText == text {
32
		return nil, nil
33
	}
34
35
	endLine, endChar := getEndLineAndChar(text)
36
	return []protocol.TextEdit{{
37
		NewText: newText,
38
		Range: protocol.Range{
39
			Start: protocol.Position{
40
				Line:      0,
41
				Character: 0,
42
			},
43
			End: protocol.Position{
44
				Line:      uint32(endLine),
45
				Character: uint32(endChar),
46
			},
47
		},
48
	}}, nil
49
}
50
51
func getEndLineAndChar(txt string) (int, int) {
52
	endLine := 0
53
	lastNewline := -1
54
	for i, ch := range txt {
55
		if ch == '\n' {
56
			endLine++
57
			lastNewline = i
58
		}
59
	}
60
	endChar := 0
61
	if lastNewline >= 0 {
62
		if lastNewline == len(txt)-1 {
63
			endLine++
64
			endChar = 0
65
		} else {
66
			endChar = len(txt) - lastNewline - 1
67
		}
68
	} else {
69
		endChar = len(txt)
70
	}
71
	return endLine, endChar
72
}