all repos

clerk @ 78d7e880287a297d48d2667f5b4cd3448b4835ff

missing tooling for ledger/hledger
8 files changed, 116 insertions(+), 26 deletions(-)
printer: add commodity-space option
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-09-26 17:07:40 +0300
Authored at: 2026-09-25 23:03:28 +0300
Change ID: szqykqltlqvpomtqkyuwkrkumwnutvqt
Parent: 4481c7d
M internal/settings/format.go
···
        43
        43
         		}

      
        44
        44
         		switch {

      
        45
        45
         		case strings.EqualFold(c, "after"):

      
        46
        
        -			s.Format.CommodityPos = printer.CommodityAfter

      
        
        46
        +			s.Format.CommodityPos = printer.CommodityPosAfter

      
        47
        47
         		case strings.EqualFold(c, "before"):

      
        48
        
        -			s.Format.CommodityPos = printer.CommodityBefore

      
        
        48
        +			s.Format.CommodityPos = printer.CommodityPosBefore

      
        49
        49
         		default:

      
        50
        50
         			return nil, fmt.Errorf("invalid value %q (want %q or %q)", c, "after", "before")

      
        
        51
        +		}

      
        
        52
        +	case "commodity_space":

      
        
        53
        +		c, ok := val.(string)

      
        
        54
        +		if !ok {

      
        
        55
        +			return nil, fmt.Errorf("invalid value %v (want string)", val)

      
        
        56
        +		}

      
        
        57
        +		switch {

      
        
        58
        +		case strings.EqualFold(c, "preserve"):

      
        
        59
        +			s.Format.CommoditySpace = printer.CommoditySpacePreserve

      
        
        60
        +		case strings.EqualFold(c, "always"):

      
        
        61
        +			s.Format.CommoditySpace = printer.CommoditySpaceAlways

      
        
        62
        +		case strings.EqualFold(c, "never"):

      
        
        63
        +			s.Format.CommoditySpace = printer.CommoditySpaceNever

      
        
        64
        +		default:

      
        
        65
        +			return nil, fmt.Errorf("invalid value %q (want %q, %q or %q)", c, "preserve", "always", "never")

      
        51
        66
         		}

      
        52
        67
         	default:

      
        53
        68
         		return []string{fmt.Sprintf("unknown format option %q", name)}, nil

      
M journal/printer/amount.go
···
        32
        32
         	}

      
        33
        33
         

      
        34
        34
         	switch p.cfg.CommodityPos {

      
        35
        
        -	case CommodityBefore:

      
        
        35
        +	case CommodityPosBefore:

      
        36
        36
         		p.buf.WriteString(comm)

      
        37
        
        -		if a.HasSpace {

      
        38
        
        -			p.buf.WriteByte(' ')

      
        39
        
        -		}

      
        
        37
        +		p.writeCommoditySpace(a)

      
        40
        38
         		p.writeDecimal(a.Quantity, a.QuantityFmt, prec)

      
        41
        
        -	case CommodityAfter:

      
        
        39
        +	case CommodityPosAfter:

      
        42
        40
         		p.writeDecimal(a.Quantity, a.QuantityFmt, prec)

      
        
        41
        +		p.writeCommoditySpace(a)

      
        
        42
        +		p.buf.WriteString(comm)

      
        
        43
        +	default:

      
        
        44
        +		panic("impossible CommodityPos value")

      
        
        45
        +	}

      
        
        46
        +}

      
        
        47
        +

      
        
        48
        +func (p *printer) writeCommoditySpace(a *ast.Amount) {

      
        
        49
        +	switch p.cfg.CommoditySpace {

      
        
        50
        +	case CommoditySpacePreserve:

      
        43
        51
         		if a.HasSpace {

      
        44
        52
         			p.buf.WriteByte(' ')

      
        45
        53
         		}

      
        46
        
        -		p.buf.WriteString(comm)

      
        
        54
        +	case CommoditySpaceAlways:

      
        
        55
        +		p.buf.WriteByte(' ')

      
        
        56
        +	case CommoditySpaceNever:

      
        47
        57
         	default:

      
        48
        
        -		panic("impossible CommodityPos value")

      
        
        58
        +		panic("impossible CommoditySpace value")

      
        49
        59
         	}

      
        50
        60
         }

      
        51
        61
         

      
M journal/printer/printer.go
···
        34
        34
         type CommodityPos int

      
        35
        35
         

      
        36
        36
         const (

      
        37
        
        -	CommodityAfter  CommodityPos = iota // "10.00 EUR"

      
        38
        
        -	CommodityBefore                     // "$10.00"

      
        
        37
        +	CommodityPosAfter  CommodityPos = iota // "10.00 EUR"

      
        
        38
        +	CommodityPosBefore                     // "$10.00"

      
        39
        39
         )

      
        40
        40
         

      
        41
        41
         func (c CommodityPos) String() string {

      
        42
        42
         	switch c {

      
        43
        
        -	case CommodityAfter:

      
        
        43
        +	case CommodityPosAfter:

      
        44
        44
         		return "after"

      
        45
        
        -	case CommodityBefore:

      
        
        45
        +	case CommodityPosBefore:

      
        46
        46
         		return "before"

      
        47
        47
         	default:

      
        48
        48
         		panic("invalid commodity pos value")

      
        49
        49
         	}

      
        50
        50
         }

      
        
        51
        +

      
        
        52
        +// CommoditySpace controls the space between quantity and commodity

      
        
        53
        +type CommoditySpace int

      
        
        54
        +

      
        
        55
        +const (

      
        
        56
        +	CommoditySpacePreserve CommoditySpace = iota // "$10" / "$ 10"

      
        
        57
        +	CommoditySpaceAlways                         // "$ 10"

      
        
        58
        +	CommoditySpaceNever                          // "$10"

      
        
        59
        +)

      
        51
        60
         

      
        52
        61
         type Config struct {

      
        53
        
        -	TabIndent          bool         // true = tabs, false = spaces

      
        54
        
        -	IndentWidth        int          // spaces per indent level (default: 2)

      
        55
        
        -	PreserveBlankLines bool         // preserve consecutive blank lines as-is

      
        56
        
        -	AlignStyle         AlignStyle   // (default AlignTwoSpaces)

      
        57
        
        -	AlignColumn        int          // fixed column for AlignRight

      
        58
        
        -	CommodityPos       CommodityPos // where to place commodity

      
        
        62
        +	TabIndent          bool           // true = tabs, false = spaces

      
        
        63
        +	IndentWidth        int            // spaces per indent level (default: 2)

      
        
        64
        +	PreserveBlankLines bool           // preserve consecutive blank lines as-is

      
        
        65
        +	AlignStyle         AlignStyle     // (default AlignTwoSpaces)

      
        
        66
        +	AlignColumn        int            // fixed column for AlignRight

      
        
        67
        +	CommodityPos       CommodityPos   // where to place commodity

      
        
        68
        +	CommoditySpace     CommoditySpace // space between quantity and commodity

      
        59
        69
         }

      
        60
        70
         

      
        61
        71
         var DefaultConfig = Config{

      ···
        64
        74
         	PreserveBlankLines: false,

      
        65
        75
         	AlignStyle:         AlignTwoSpaces,

      
        66
        76
         	AlignColumn:        70,

      
        67
        
        -	CommodityPos:       CommodityAfter,

      
        
        77
        +	CommodityPos:       CommodityPosAfter,

      
        
        78
        +	CommoditySpace:     CommoditySpacePreserve,

      
        68
        79
         }

      
        69
        80
         

      
        70
        81
         func (c *Config) indent() string {

      
M journal/printer/printer_test.go
···
        45
        45
         }

      
        46
        46
         

      
        47
        47
         var testsWithConfig = map[string]*Config{

      
        48
        
        -	"align_right":      {AlignStyle: AlignRight, AlignColumn: 50},

      
        49
        
        -	"align_tab":        {AlignStyle: AlignTab, AlignColumn: 50},

      
        50
        
        -	"commodity_before": {CommodityPos: CommodityBefore},

      
        51
        
        -	"preserve_blanks":  {PreserveBlankLines: true},

      
        52
        
        -	"tab_indent":       {TabIndent: true},

      
        53
        
        -	"indent_width":     {IndentWidth: 4},

      
        
        48
        +	"align_right":              {AlignStyle: AlignRight, AlignColumn: 50},

      
        
        49
        +	"align_tab":                {AlignStyle: AlignTab, AlignColumn: 50},

      
        
        50
        +	"commodity_pos_before":     {CommodityPos: CommodityPosBefore},

      
        
        51
        +	"preserve_blanks":          {PreserveBlankLines: true},

      
        
        52
        +	"commodity_space_preserve": {CommoditySpace: CommoditySpacePreserve},

      
        
        53
        +	"commodity_space_always":   {CommoditySpace: CommoditySpaceAlways},

      
        
        54
        +	"commodity_space_never":    {CommoditySpace: CommoditySpaceNever},

      
        
        55
        +	"tab_indent":               {TabIndent: true},

      
        
        56
        +	"indent_width":             {IndentWidth: 4},

      
        54
        57
         }

      
        55
        58
         

      
        56
        59
         func TestRoundTrip_WithConfig(t *testing.T) {

      
A journal/printer/testdata/commodity_space_always.txtar
···
        
        1
        +-- input --

      
        
        2
        +2026-01-15 * Groceries

      
        
        3
        +  Expenses:Food  50.00$

      
        
        4
        +  Assets:Cash

      
        
        5
        +

      
        
        6
        +2026-01-16 Salary

      
        
        7
        +  Assets:Cash  2000.00$

      
        
        8
        +  Income:Salary  -2000.00 $

      
        
        9
        +

      
        
        10
        +-- expect --

      
        
        11
        +2026-01-15 * Groceries

      
        
        12
        +  Expenses:Food  50.00 $

      
        
        13
        +  Assets:Cash

      
        
        14
        +

      
        
        15
        +2026-01-16 Salary

      
        
        16
        +  Assets:Cash  2000.00 $

      
        
        17
        +  Income:Salary  -2000.00 $

      
A journal/printer/testdata/commodity_space_never.txtar
···
        
        1
        +-- input --

      
        
        2
        +2026-01-15 * Groceries

      
        
        3
        +  Expenses:Food  50.00 $

      
        
        4
        +  Assets:Cash

      
        
        5
        +

      
        
        6
        +2026-01-16 Salary

      
        
        7
        +  Assets:Cash  2000.00$

      
        
        8
        +  Income:Salary  -2000.00 $

      
        
        9
        +

      
        
        10
        +-- expect --

      
        
        11
        +2026-01-15 * Groceries

      
        
        12
        +  Expenses:Food  50.00$

      
        
        13
        +  Assets:Cash

      
        
        14
        +

      
        
        15
        +2026-01-16 Salary

      
        
        16
        +  Assets:Cash  2000.00$

      
        
        17
        +  Income:Salary  -2000.00$

      
A journal/printer/testdata/commodity_space_preserve.txtar
···
        
        1
        +-- input --

      
        
        2
        +2026-01-15 * Groceries

      
        
        3
        +  Expenses:Food  50.00$

      
        
        4
        +  Assets:Cash

      
        
        5
        +

      
        
        6
        +2026-01-16 Salary

      
        
        7
        +  Assets:Cash  $ 2000.00

      
        
        8
        +  Income:Salary  -2000.00 $

      
        
        9
        +

      
        
        10
        +-- expect --

      
        
        11
        +2026-01-15 * Groceries

      
        
        12
        +  Expenses:Food  50.00$

      
        
        13
        +  Assets:Cash

      
        
        14
        +

      
        
        15
        +2026-01-16 Salary

      
        
        16
        +  Assets:Cash  2000.00 $

      
        
        17
        +  Income:Salary  -2000.00 $