5 files changed,
85 insertions(+),
9 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-07-12 13:17:30 +0300
Authored at:
2026-07-11 13:35:22 +0300
Change ID:
uowsszmkskwqvokqzoxqpwnurkvwwlnw
Parent:
3d92c0e
M
internal/lsp/lsp.go
··· 12 12 13 13 "olexsmir.xyz/clerk/internal/linter" 14 14 "olexsmir.xyz/clerk/journal" 15 + "olexsmir.xyz/clerk/journal/printer" 15 16 ) 16 17 17 18 type Server struct{ server *server } ··· 24 25 25 26 openDocs: make(map[uri.URI]docState), 26 27 27 - linter: linter.NewLinter(linter.Rules), 28 - loader: journal.NewLoader(), 28 + linter: linter.NewLinter(linter.Rules), 29 + loader: journal.NewLoader(), 30 + printer: printer.DefaultConfig, 29 31 30 32 log: slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{})), 31 33 },
M
internal/lsp/server.go
··· 11 11 12 12 "olexsmir.xyz/clerk/internal/linter" 13 13 "olexsmir.xyz/clerk/journal" 14 + "olexsmir.xyz/clerk/journal/printer" 14 15 "olexsmir.xyz/clerk/journal/semantic" 15 16 ) 16 17 ··· 24 25 25 26 linter *linter.Linter 26 27 loader *journal.Loader 28 + printer *printer.Config 27 29 debouncer *time.Timer 28 30 29 31 mu sync.Mutex
A
internal/lsp/textdocument_format.go
··· 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 +}
M
journal/printer/printer.go
··· 34 34 CommodityPos CommodityPos // where to place commodity 35 35 } 36 36 37 -var defaultConfig = &Config{ 37 +var DefaultConfig = &Config{ 38 38 TabIndent: false, 39 39 IndentWidth: 2, 40 40 PreserveBlankLines: false, ··· 63 63 } 64 64 65 65 // Fprint formats using the default config. 66 -func Fprint(w io.Writer, j *ast.Journal) error { return defaultConfig.Fprint(w, j) } 66 +func Fprint(w io.Writer, j *ast.Journal) error { return DefaultConfig.Fprint(w, j) } 67 67 68 68 // Fprint formats a parsed journal. 69 69 func (c *Config) Fprint(w io.Writer, j *ast.Journal) error { 70 70 if c == nil { 71 - c = defaultConfig 71 + c = DefaultConfig 72 72 } 73 73 p := printer{cfg: c, indent: c.indent()} 74 74 ··· 83 83 } 84 84 85 85 // FprintEntry formats a single ast entry using the default config. 86 -func FprintEntry(w io.Writer, e ast.Entry) error { return defaultConfig.FprintEntry(w, e) } 86 +func FprintEntry(w io.Writer, e ast.Entry) error { return DefaultConfig.FprintEntry(w, e) } 87 87 88 88 // FprintEntry formats a single journal entry. 89 89 func (c *Config) FprintEntry(w io.Writer, e ast.Entry) error { 90 90 if c == nil { 91 - c = defaultConfig 91 + c = DefaultConfig 92 92 } 93 93 p := printer{cfg: c, indent: c.indent()} 94 94 p.formatEntry(e)
M
journal/printer/printer_test.go
··· 20 20 t.Fatal(err) 21 21 } 22 22 var b strings.Builder 23 - if err := defaultConfig.Fprint(&b, pf.Ast); err != nil { 23 + if err := DefaultConfig.Fprint(&b, pf.Ast); err != nil { 24 24 t.Fatal(err) 25 25 } 26 26 ··· 41 41 b.ResetTimer() 42 42 for i := 0; i < b.N; i++ { 43 43 var buf strings.Builder 44 - if err := defaultConfig.Fprint(&buf, pf.Ast); err != nil { 44 + if err := DefaultConfig.Fprint(&buf, pf.Ast); err != nil { 45 45 b.Fatal(err) 46 46 } 47 47 }