acidtab

package module
v0.0.0-...-8b7ea07 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2021 License: MIT Imports: 5 Imported by: 0

README

Go package to print nicely aligned tables in the terminal.

Import at arp242.net/acidtab. Godoc: https://pkg.go.dev/arp242.net/acidtab

You can view all these examples in your terminal with go test -v.

Basic usage

// Create a new table
t := acidtab.New("Name", "Origin", "Job", "Speciality", "Alive")

// Add rows
t.Row("James Holden", "Montana", "Captain", "Tilting windmills", true)
t.Row("Amos Burton", "Baltimore", "Mechanic", "Specific people skills", true)

// And then print it:
t.Horizontal(os.Stdout)

Outputs:

      Name      │   Origin    │    Job     │        Speciality        │  Alive
────────────────┼─────────────┼────────────┼──────────────────────────┼─────────
  James Holden  │  Montana    │  Captain   │  Tilting windmills       │  true
  Amos Burton   │  Baltimore  │  Mechanic  │  Specific people skills  │  true

Note: because GitHub adds a line-height there are little gaps between the vertical dividers. You don't have this in a terminal.

Options

There are a bunch of options you can set:

t := acidtab.New("Name", "Origin", "Job", "Speciality", "Alive")

t.Borders(acidtab.BordersHeavy)               // Set different borders.
t.Pad(" ")                                  // Pad cells with one space.
t.Prefix(" ")                               // Prefix every line with a space.
t.Close(acidtab.CloseTop | acidtab.CloseBottom) // "Close" top and bottom.
t.Header(false)                             // Don't print the header.

t.Row("Naomi Nagata", "Pallas", "Mechanic", "Spicy red food", true)
t.Row("Alex Kamal", "Mars", "Pilot", "Cowboys", false)

t.Horizontal(os.Stdout)

Outputs:

 ━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━
  Naomi Nagata ┃ Pallas ┃ Mechanic ┃ Spicy red food ┃ true
  Alex Kamal   ┃ Mars   ┃ Pilot    ┃ Cowboys        ┃ false
 ━━━━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━
  • The default for Pad() is two spaces.

  • The default for Prefix() is an empty string.

  • acidtab.CloseLeft and acidtab.CloseRightcan be used to add borders to the left and right too, oracidtab.CloseAll` to add borders to all sides.

  • The default for Borders is acidtab.BordersHeavy; also see the other Borders* variables, and you can define you own. Note you need to define all used characters, otherwise it will print the zero value (a NULL byte).

Column options

You can set options for columns:

t := acidtab.New("Name", "Origin", "Job", "Speciality", "Alive")
t.Close(acidtab.CloseLeft | acidtab.CloseRight)

t.AlignCol(3, acidtab.Right) // Align column 3 and 4 (starts at 0)
t.AlignCol(4, acidtab.Center)

t.PrintCol(3, "%q") // Print column 3 as %q

// Callback for column 4
t.PrintFuncCol(4, func(v interface{}) string {
    if b, ok := v.(bool); ok {
        return map[bool]string{true: "yes", false: "no"}[b]
    }
    // Return a NULL byte to fall back to regular formatting.
    return "\x00"
})

t.Row("Joe Miller", "Ceres", "Cop", "Doors 'n corners", false)
t.Row("Chrisjen Avasarala", "Earth", "Politician", "Insults", true)

t.Horizontal(os.Stdout)

Outputs:

│         Name         │  Origin  │     Job      │      Speciality      │  Alive  │
├──────────────────────┼──────────┼──────────────┼──────────────────────┼─────────┤
│  Joe Miller          │  Ceres   │  Cop         │  "Doors 'n corners"  │   no    │
│  Chrisjen Avasarala  │  Earth   │  Politician  │           "Insults"  │   yes   │
  • AlignCol() sets the column alignment; the default is Auto, which right-aligns numbers (int, float) and left-aligns everything else.

  • PrintCol() sets how the value is printed with fmt.Sprintf. The default if %v.

  • PrintColFunc() sets a callback to print the value instead, in this case to show something nicer than "true" or "false". Return a NULL byte to fall back to fmt.Sprintf formatting.

    acidtab.PrintAsNum can be used to print numbers with thousands separators (only , at the moment, not locale-aware). e.g. PrintFuncCol(1, acidtab.PrintAsNum).

The column indexes start at zero. Note these are not checked: if you defined fewer headers then you will get a panic.

Vertical table

You can print a "vertical" table with Vertical():

t := acidtab.New("Name", "Origin", "Job", "Speciality", "Alive")
t.Row("Prax Meng", "Ganymede", "Botanist", "Plant metaphors", true)
t.Row("Klaes Ashford", "The belt", "Pirate", "Singing", "😢")
t.Vertical(os.Stdout)

Outputs:

  Name        │  Prax Meng
  Origin      │  Ganymede
  Job         │  Botanist
  Speciality  │  Plant metaphors
  Alive       │  true
──────────────┼───────────────────
  Name        │  Klaes Ashford
  Origin      │  The belt
  Job         │  Pirate
  Speciality  │  Singing
  Alive       │  😢

Most options for horizontal tables work for this as well, but disabling the header has no effect (it would be a bit pointless) and data is always left-aligned.

Chaining

All options can be chained:

acidtab.New("Name", "Origin", "Job", "Speciality", "Alive").
    Close(acidtab.CloseTop|acidtab.CloseBottom).
    Prefix(" ").
    Pad(" ").
    PrintCol(1, "%q").
    Rows(
        "Adolphus Murtry", "Earth", "Security", "General twattery", false,
        "Fred Johnson", "Earth", "Colonol", "Beltalowda", false,
    ).
    Vertical(os.Stdout)

Outputs:

 ────────────┬──────────────────
  Name       │ Adolphus Murtry
  Origin     │ "Earth"
  Job        │ Security
  Speciality │ General twattery
  Alive      │ false
 ────────────┼──────────────────
  Name       │ Fred Johnson
  Origin     │ "Earth"
  Job        │ Colonol
  Speciality │ Beltalowda
  Alive      │ false
 ────────────┴──────────────────

It's identical to the above examples; you don't need the return value. It's just a bit more convenient in some cases.

Also note the usage of Rows() to add multiple rows in the above example.

Formatting

You can use plain ol' escape sequences to format stuff. This won't interfere with alignment:

t := acidtab.New(bold("Name"), bold("Origin"), bold("Job"), bold("Speciality"), bold("Alive")).
    Close(acidtab.CloseAll).
    AlignCol(4, acidtab.Center).
    PrintFuncCol(4, func(v interface{}) string {
        if b, ok := v.(bool); ok {
            return map[bool]string{
                true:  "\x1b[32m ✔ \x1b[0m",
                false: "\x1b[31m✘\x1b[0m",
            }[b]
        }
        return "\x00"
    })

t.Rows(
    "James Holden", "Montana 🌎", "Captain 🚀", "Tilting windmills", true,
    "Amos Burton", "Baltimore 🌎", "Mechanic 🔧", "Specific people skills", true,
    "Naomi Nagata", "Pallas 🌌", "Mechanic 💻", "Spicy red food", true,
    "Alex Kamal", "Mars 🔴", "Pilot 🎧", "Cowboys", false,
    "Joe Miller", "Ceres 🌌", "Cop 👮", "Doors 'n corners", true,
    "Chrisjen Avasarala", "Earth 🌏", "Politician 🖕", "Insults", true,
    "Prax Meng", "Ganymede 🌌", "Botanist 🌻", "Plant metaphors", true,
    "Klaes Ashford", "The belt 🌌", "Pirate 🕱", "Singing", "😢",
    "Adolphus Murtry", "Earth 🌎", "Security 💂", "General twattery", false,
    "Fred Johnson", "Earth 🌎", "Colonol 🎖", "Beltalowda", false)

t.Horizontal(os.Stdout)

Outputs (as screenshot as I can't get GitHub to render this correct):

Note that everything is aligned correctly, even though there are escape sequences, multi-codepoint emojis, and double-width emojis. As long as the terminal renders this type of thing correctly this should work for most of Unicode (including east-Asian characters). Unfortunately, many terminals render things like emojis inconsistently, so it's probably best to avoid them (but it looks cute in the demo).

Documentation

Overview

Package acidtab prints aligned tables.

Example (Basic)
package main

import (
	"os"

	"arp242.net/acidtab"
)

func main() {
	// Create a new table
	t := acidtab.New("Name", "Origin", "Job", "Speciality", "Alive")

	// Add rows to it
	t.Row("James Holden", "Montana", "Captain", "Tilting windmills", true)
	t.Row("Amos Burton", "Baltimore", "Mechanic", "Specific people skills", true)

	// And then print it:
	t.Horizontal(os.Stdout)

}
Output:

      Name      │   Origin    │    Job     │        Speciality        │  Alive
────────────────┼─────────────┼────────────┼──────────────────────────┼─────────
  James Holden  │  Montana    │  Captain   │  Tilting windmills       │  true
  Amos Burton   │  Baltimore  │  Mechanic  │  Specific people skills  │  true
Example (Chain)
package main

import (
	"os"

	"arp242.net/acidtab"
)

func main() {
	acidtab.New("Name", "Origin", "Job", "Speciality", "Alive").
		Close(acidtab.CloseTop|acidtab.CloseBottom).
		Prefix(" ").
		Pad(" ").
		PrintCol(1, "%q").
		Rows(
			"Adolphus Murtry", "Earth", "Security", "General twattery", false,
			"Fred Johnson", "Earth", "Colonol", "Beltalowda", false,
		).
		Vertical(os.Stdout)

}
Output:

 ────────────┬──────────────────
  Name       │ Adolphus Murtry
  Origin     │ "Earth"
  Job        │ Security
  Speciality │ General twattery
  Alive      │ false
 ────────────┼──────────────────
  Name       │ Fred Johnson
  Origin     │ "Earth"
  Job        │ Colonol
  Speciality │ Beltalowda
  Alive      │ false
 ────────────┴──────────────────
Example (Coloptions)
package main

import (
	"os"

	"arp242.net/acidtab"
)

func main() {
	t := acidtab.New("Name", "Origin", "Job", "Speciality", "Alive")
	t.Close(acidtab.CloseLeft | acidtab.CloseRight)

	t.AlignCol(3, acidtab.Right) // Align column 3 and 4 (starts at 0)
	t.AlignCol(4, acidtab.Center)

	t.PrintCol(3, "%q") // Print column 3 as %q

	// Callback for column 4
	t.PrintFuncCol(4, func(v interface{}) string {
		if b, ok := v.(bool); ok {
			return map[bool]string{true: "yes", false: "no"}[b]
		}
		// Return a NULL byte to fall back to regular formatting.
		return "\x00"
	})

	t.Row("Joe Miller", "Ceres", "Cop", "Doors 'n corners", false)
	t.Row("Chrisjen Avasarala", "Earth", "Politician", "Insults", true)

	t.Horizontal(os.Stdout)

}
Output:

│         Name         │  Origin  │     Job      │      Speciality      │  Alive  │
├──────────────────────┼──────────┼──────────────┼──────────────────────┼─────────┤
│  Joe Miller          │  Ceres   │  Cop         │  "Doors 'n corners"  │   no    │
│  Chrisjen Avasarala  │  Earth   │  Politician  │           "Insults"  │   yes   │
Example (Format)
package main

import (
	"os"

	"arp242.net/acidtab"
)

func main() {
	bold := func(s string) string { return "\x1b[1m" + s + "\x1b[0m" }

	t := acidtab.New(bold("Name"), bold("Origin"), bold("Job"), bold("Speciality"), bold("Alive")).
		Close(acidtab.CloseAll).
		AlignCol(4, acidtab.Center).
		PrintFuncCol(4, func(v interface{}) string {
			if b, ok := v.(bool); ok {
				return map[bool]string{
					true:  "\x1b[32m ✔ \x1b[0m",
					false: "\x1b[31m✘\x1b[0m",
				}[b]
			}
			return "\x00"
		})

	t.Rows(
		"James Holden", "Montana 🌎", "Captain 🚀", "Tilting windmills", true,
		"Amos Burton", "Baltimore 🌎", "Mechanic 🔧", "Specific people skills", true,
		"Naomi Nagata", "Pallas 🌌", "Mechanic 💻", "Spicy red food", true,
		"Alex Kamal", "Mars 🔴", "Pilot 🎧", "Cowboys", false,
		"Joe Miller", "Ceres 🌌", "Cop 👮", "Doors 'n corners", true,
		"Chrisjen Avasarala", "Earth 🌏", "Politician 🖕", "Insults", true,
		"Prax Meng", "Ganymede 🌌", "Botanist 🌻", "Plant metaphors", true,
		"Klaes Ashford", "The belt 🌌", "Pirate 🕱", "Singing", "😢",
		"Adolphus Murtry", "Earth 🌎", "Security 💂", "General twattery", false,
		"Fred Johnson", "Earth 🌎", "Colonol 🎖", "Beltalowda", false)

	t.Horizontal(os.Stdout)
}
Output:

┌──────────────────────┬────────────────┬─────────────────┬──────────────────────────┬─────────┐
│         �[1mName�[0m         │     �[1mOrigin�[0m     │       �[1mJob�[0m       │        �[1mSpeciality�[0m        │  �[1mAlive�[0m  │
├──────────────────────┼────────────────┼─────────────────┼──────────────────────────┼─────────┤
│  James Holden        │  Montana 🌎    │  Captain 🚀     │  Tilting windmills       │   �[32m ✔ �[0m   │
│  Amos Burton         │  Baltimore 🌎  │  Mechanic 🔧    │  Specific people skills  │   �[32m ✔ �[0m   │
│  Naomi Nagata        │  Pallas 🌌     │  Mechanic 💻    │  Spicy red food          │   �[32m ✔ �[0m   │
│  Alex Kamal          │  Mars 🔴       │  Pilot 🎧       │  Cowboys                 │    �[31m✘�[0m    │
│  Joe Miller          │  Ceres 🌌      │  Cop 👮         │  Doors 'n corners        │   �[32m ✔ �[0m   │
│  Chrisjen Avasarala  │  Earth 🌏      │  Politician 🖕  │  Insults                 │   �[32m ✔ �[0m   │
│  Prax Meng           │  Ganymede 🌌   │  Botanist 🌻    │  Plant metaphors         │   �[32m ✔ �[0m   │
│  Klaes Ashford       │  The belt 🌌   │  Pirate 🕱       │  Singing                 │   😢    │
│  Adolphus Murtry     │  Earth 🌎      │  Security 💂    │  General twattery        │    �[31m✘�[0m    │
│  Fred Johnson        │  Earth 🌎      │  Colonol 🎖      │  Beltalowda              │    �[31m✘�[0m    │
└──────────────────────┴────────────────┴─────────────────┴──────────────────────────┴─────────┘
Example (Options)
package main

import (
	"os"

	"arp242.net/acidtab"
)

func main() {
	t := acidtab.New("Name", "Origin", "Job", "Speciality", "Alive")

	t.Borders(acidtab.BordersHeavy)                 // Set different borders.
	t.Pad(" ")                                      // Pad cells with one space.
	t.Prefix(" ")                                   // Prefix every line with a space.
	t.Close(acidtab.CloseTop | acidtab.CloseBottom) // "Close" top and bottom.
	t.Header(false)                                 // Don't print the header.

	t.Row("Naomi Nagata", "Pallas", "Mechanic", "Spicy red food", true)
	t.Row("Alex Kamal", "Mars", "Pilot", "Cowboys", false)

	t.Horizontal(os.Stdout)

}
Output:

 ━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━
  Naomi Nagata ┃ Pallas ┃ Mechanic ┃ Spicy red food ┃ true
  Alex Kamal   ┃ Mars   ┃ Pilot    ┃ Cowboys        ┃ false
 ━━━━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━
Example (Vertical)
package main

import (
	"os"

	"arp242.net/acidtab"
)

func main() {
	t := acidtab.New("Name", "Origin", "Job", "Speciality", "Alive")
	t.Row("Prax Meng", "Ganymede", "Botanist", "Plant metaphors", true)
	t.Row("Klaes Ashford", "The belt", "Pirate", "Singing", "😢")
	t.Vertical(os.Stdout)

}
Output:

  Name        │  Prax Meng
  Origin      │  Ganymede
  Job         │  Botanist
  Speciality  │  Plant metaphors
  Alive       │  true
──────────────┼───────────────────
  Name        │  Klaes Ashford
  Origin      │  The belt
  Job         │  Pirate
  Speciality  │  Singing
  Alive       │  😢

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	BordersDefault = Borders{'─', '│', '┼', '┌', '┐', '└', '┘', '├', '┤', '┬', '┴'}
	BordersHeavy   = Borders{'━', '┃', '╋', '┏', '┓', '┗', '┛', '┣', '┫', '┳', '┻'}
	BordersASCII   = Borders{'-', '|', '+', '+', '+', '+', '+', '+', '+', '+', '+'}
	BordersSpace   = Borders{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}
)

Characters to use to draw the borders.

Functions

func PrintAsNum

func PrintAsNum(n interface{}) string

Print as a number with , as thousands separators.

Types

type Align

type Align uint8 // Alignment for columns.
const (
	Auto Align = iota
	Left
	Right
	Center
)

Column alignment.

type Borders

type Borders struct {
	Line, Bar, Cross                           rune
	TopLeft, TopRight, BottomLeft, BottomRight rune
	BarRight, BarLeft, LineTop, LineBottom     rune
}

Borders to use.

type Close

type Close uint8 // Which sides of the table to "close".
const (
	CloseBottom Close = 1 << iota
	CloseTop
	CloseLeft
	CloseRight
	CloseAll Close = CloseBottom | CloseTop | CloseLeft | CloseRight
)

Which sides to close.

type PrintAs

type PrintAs string // How to print a value.

type PrintAsFunc

type PrintAsFunc func(v interface{}) string

func PrintAsFloat

func PrintAsFloat(perc int) PrintAsFunc

type Table

type Table struct {
	// contains filtered or unexported fields
}

func New

func New(header ...string) *Table

New creates a new table with the given headers.

func (*Table) AlignCol

func (t *Table) AlignCol(n int, a Align) *Table

func (*Table) Borders

func (t *Table) Borders(borders Borders) *Table

func (*Table) Close

func (t *Table) Close(close Close) *Table

func (*Table) Grow

func (t *Table) Grow(n int)

Grow the rows allocation.

func (*Table) Header

func (t *Table) Header(on bool) *Table

func (Table) Horizontal

func (t Table) Horizontal(w io.Writer)

func (*Table) Pad

func (t *Table) Pad(pad string) *Table

func (*Table) Prefix

func (t *Table) Prefix(prefix string) *Table

func (*Table) PrintCol

func (t *Table) PrintCol(n int, p PrintAs) *Table

func (*Table) PrintFuncCol

func (t *Table) PrintFuncCol(n int, p PrintAsFunc) *Table

func (*Table) Row

func (t *Table) Row(r ...interface{}) *Table

Row adds a new row.

The number of values can be lower than the number of headers; the remaining cells will be filled with spaces.

If the number of values is greater it will panic.

func (*Table) Rows

func (t *Table) Rows(r ...interface{}) *Table

Rows adds multiple rows; the number of values should be an exact multitude of the number of headers.

For example:

t.Rows(
    "row1", "row1",
    "row2", "row2",)

func (Table) String

func (t Table) String() string

func (Table) Vertical

func (t Table) Vertical(w io.Writer)

Vertical prints the table as vertical.

Data is always left-aligned, and Header(false) has no effect.

func (*Table) Width

func (t *Table) Width() int

Width gets the display width of the table, including any padding characters.

The width may grow if more rows are added.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL