all repos

clerk @ 6fdb9097048e212574439fb0da84d0c94aa7e01b

missing tooling for ledger/hledger

clerk/journal/printer/postings.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
formatter, 17 hours ago
1
package printer
2
3
import (
4
	"fmt"
5
	"strings"
6
	"text/tabwriter"
7
8
	"olexsmir.xyz/clerk/journal/ast"
9
)
10
11
func (p *printer) writePostings(postings []*ast.Posting) {
12
	if len(postings) == 0 {
13
		return
14
	}
15
16
	maxAcct := measureTxAccts(postings)
17
	if p.cfg.AlignStyle == AlignTwoSpaces {
18
		p.writePostingsTwoSpaces(postings, maxAcct)
19
	} else {
20
		p.writePostingsTabbed(postings, maxAcct)
21
	}
22
}
23
24
func (p *printer) writePostingsTwoSpaces(postings []*ast.Posting, maxAcct int) {
25
	for _, pt := range postings {
26
		p.writePostingLine(pt, maxAcct)
27
		p.buf.WriteByte('\n')
28
		for _, c := range pt.Comments {
29
			p.buf.WriteString(p.indent)
30
			p.writeComment(&c)
31
			p.buf.WriteByte('\n')
32
		}
33
	}
34
}
35
36
func (p *printer) writePostingsTabbed(postings []*ast.Posting, maxAcct int) {
37
	var tmp strings.Builder
38
	tw := tabwriter.NewWriter(&tmp, 0, 0, 2, ' ', tabwriter.StripEscape)
39
40
	lp := &printer{cfg: p.cfg, indent: p.indent}
41
	for _, pt := range postings {
42
		lp.buf.Reset()
43
		lp.writePostingLine(pt, maxAcct)
44
		fmt.Fprintln(tw, lp.buf.String())
45
		for _, c := range pt.Comments {
46
			lp.buf.Reset()
47
			lp.buf.WriteString(lp.indent)
48
			lp.writeComment(&c)
49
			fmt.Fprintln(tw, lp.buf.String())
50
		}
51
	}
52
	tw.Flush()
53
54
	out := strings.TrimRight(tmp.String(), "\n")
55
	if out == "" {
56
		return
57
	}
58
	p.buf.WriteString(out)
59
	p.buf.WriteByte('\n')
60
}
61
62
func (p *printer) writePostingLine(pt *ast.Posting, maxAcct int) {
63
	p.buf.WriteString(p.indent)
64
65
	if pt.Status != nil && pt.Status.Value != ast.StatusNone {
66
		p.buf.WriteString(pt.Status.Value.String())
67
		p.buf.WriteByte(' ')
68
	}
69
70
	acct := pt.Account.Name
71
	switch pt.Type {
72
	case ast.PostingVirtualUnbalanced:
73
		acct = "(" + acct + ")"
74
	case ast.PostingVirtualBalanced:
75
		acct = "[" + acct + "]"
76
	}
77
	p.buf.WriteString(acct)
78
79
	pos := p.cfg.CommodityPos
80
81
	if pt.Amount != nil || pt.Balance != nil {
82
		switch p.cfg.AlignStyle {
83
		case AlignTwoSpaces:
84
			p.buf.WriteString("  ")
85
		case AlignRight:
86
			current := len(p.indent) + len(acct)
87
			if pt.Status != nil && pt.Status.Value != ast.StatusNone {
88
				current++
89
			}
90
			if pad := p.cfg.AlignColumn - current; pad > 0 {
91
				p.writeSpaces(pad)
92
			}
93
			p.buf.WriteByte('\v')
94
		case AlignTab:
95
			current := len(p.indent) + len(acct)
96
			if pt.Status != nil && pt.Status.Value != ast.StatusNone {
97
				current++
98
			}
99
			if pad := maxAcct + len(p.indent) - current; pad > 0 {
100
				p.writeSpaces(pad)
101
			}
102
			p.buf.WriteByte('\v')
103
		}
104
	}
105
106
	if pt.Amount != nil {
107
		p.writeAmount(pt.Amount, pos)
108
		if pt.Cost != nil {
109
			p.writeCost(pt.Cost, pos)
110
		}
111
	}
112
113
	if pt.Balance != nil {
114
		p.writeBalanceAssertion(pt.Balance, pos)
115
	}
116
117
	if pt.Comment != nil && pt.Comment.Text != "" {
118
		if p.cfg.AlignStyle == AlignTwoSpaces {
119
			p.buf.WriteString("  ")
120
		} else {
121
			p.buf.WriteByte('\v')
122
		}
123
		p.buf.WriteString("; ")
124
		p.buf.WriteString(pt.Comment.Text)
125
	}
126
}
127
128
func measureTxAccts(postings []*ast.Posting) int {
129
	maxAcct := 0
130
	for _, p := range postings {
131
		n := len(p.Account.Name)
132
		if p.Type == ast.PostingVirtualUnbalanced || p.Type == ast.PostingVirtualBalanced {
133
			n += 2
134
		}
135
		if n > maxAcct {
136
			maxAcct = n
137
		}
138
	}
139
	return maxAcct
140
}