Skip to content

Commit f7e7add

Browse files
authored
Merge pull request #1266 from afbjorklund/hide-columns
Hide columns in the list command
2 parents 49bfb19 + 07456b9 commit f7e7add

File tree

3 files changed

+247
-7
lines changed

3 files changed

+247
-7
lines changed

cmd/limactl/list.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ package main
33
import (
44
"errors"
55
"fmt"
6+
"os"
67
"reflect"
78
"sort"
89
"strings"
910

11+
"github.com/cheggaaa/pb/v3/termutil"
1012
"github.com/lima-vm/lima/pkg/store"
13+
"github.com/mattn/go-isatty"
1114
"github.com/sirupsen/logrus"
1215
"github.com/spf13/cobra"
1316
)
@@ -55,6 +58,7 @@ func newListCommand() *cobra.Command {
5558
listCommand.Flags().Bool("list-fields", false, "List fields available for format")
5659
listCommand.Flags().Bool("json", false, "JSONify output")
5760
listCommand.Flags().BoolP("quiet", "q", false, "Only show names")
61+
listCommand.Flags().Bool("all-fields", false, "Show all fields")
5862

5963
return listCommand
6064
}
@@ -156,7 +160,21 @@ func listAction(cmd *cobra.Command, args []string) error {
156160
}
157161
}
158162

159-
return store.PrintInstances(cmd.OutOrStdout(), instances, format)
163+
allFields, err := cmd.Flags().GetBool("all-fields")
164+
if err != nil {
165+
return err
166+
}
167+
168+
options := store.PrintOptions{AllFields: allFields}
169+
out := cmd.OutOrStdout()
170+
if out == os.Stdout {
171+
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
172+
if w, err := termutil.TerminalWidth(); err == nil {
173+
options.TerminalWidth = w
174+
}
175+
}
176+
}
177+
return store.PrintInstances(out, instances, format, &options)
160178
}
161179

162180
func listBashComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

pkg/store/instance.go

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,17 +239,80 @@ func AddGlobalFields(inst *Instance) (FormatData, error) {
239239
return data, nil
240240
}
241241

242+
type PrintOptions struct {
243+
AllFields bool
244+
TerminalWidth int
245+
}
246+
242247
// PrintInstances prints instances in a requested format to a given io.Writer.
243248
// Supported formats are "json", "yaml", "table", or a go template
244-
func PrintInstances(w io.Writer, instances []*Instance, format string) error {
249+
func PrintInstances(w io.Writer, instances []*Instance, format string, options *PrintOptions) error {
245250
switch format {
246251
case "json":
247252
format = "{{json .}}"
248253
case "yaml":
249254
format = "{{yaml .}}"
250255
case "table":
256+
types := map[string]int{}
257+
archs := map[string]int{}
258+
for _, instance := range instances {
259+
types[instance.VMType]++
260+
archs[instance.Arch]++
261+
}
262+
all := options != nil && options.AllFields
263+
width := 0
264+
if options != nil {
265+
width = options.TerminalWidth
266+
}
267+
columnWidth := 8
268+
hideType := false
269+
hideArch := false
270+
hideDir := false
271+
272+
columns := 1 // NAME
273+
columns += 2 // STATUS
274+
columns += 2 // SSH
275+
// can we still fit the remaining columns (7)
276+
if width == 0 || (columns+7)*columnWidth > width && !all {
277+
hideType = len(types) == 1
278+
}
279+
if !hideType {
280+
columns++ // VMTYPE
281+
}
282+
// only hide arch if it is the same as the host arch
283+
goarch := limayaml.NewArch(runtime.GOARCH)
284+
// can we still fit the remaining columns (6)
285+
if width == 0 || (columns+6)*columnWidth > width && !all {
286+
hideArch = len(archs) == 1 && instances[0].Arch == goarch
287+
}
288+
if !hideArch {
289+
columns++ // ARCH
290+
}
291+
columns++ // CPUS
292+
columns++ // MEMORY
293+
columns++ // DISK
294+
// can we still fit the remaining columns (2)
295+
if width != 0 && (columns+2)*columnWidth > width && !all {
296+
hideDir = true
297+
}
298+
if !hideDir {
299+
columns += 2 // DIR
300+
}
301+
_ = columns
302+
251303
w := tabwriter.NewWriter(w, 4, 8, 4, ' ', 0)
252-
fmt.Fprintln(w, "NAME\tSTATUS\tSSH\tVMTYPE\tARCH\tCPUS\tMEMORY\tDISK\tDIR")
304+
fmt.Fprint(w, "NAME\tSTATUS\tSSH")
305+
if !hideType {
306+
fmt.Fprint(w, "\tVMTYPE")
307+
}
308+
if !hideArch {
309+
fmt.Fprint(w, "\tARCH")
310+
}
311+
fmt.Fprint(w, "\tCPUS\tMEMORY\tDISK")
312+
if !hideDir {
313+
fmt.Fprint(w, "\tDIR")
314+
}
315+
fmt.Fprintln(w)
253316

254317
u, err := user.Current()
255318
if err != nil {
@@ -262,17 +325,33 @@ func PrintInstances(w io.Writer, instances []*Instance, format string) error {
262325
if strings.HasPrefix(dir, homeDir) {
263326
dir = strings.Replace(dir, homeDir, "~", 1)
264327
}
265-
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\n",
328+
fmt.Fprintf(w, "%s\t%s\t%s",
266329
instance.Name,
267330
instance.Status,
268331
fmt.Sprintf("127.0.0.1:%d", instance.SSHLocalPort),
269-
instance.VMType,
270-
instance.Arch,
332+
)
333+
if !hideType {
334+
fmt.Fprintf(w, "\t%s",
335+
instance.VMType,
336+
)
337+
}
338+
if !hideArch {
339+
fmt.Fprintf(w, "\t%s",
340+
instance.Arch,
341+
)
342+
}
343+
fmt.Fprintf(w, "\t%d\t%s\t%s",
271344
instance.CPUs,
272345
units.BytesSize(float64(instance.Memory)),
273346
units.BytesSize(float64(instance.Disk)),
274-
dir,
275347
)
348+
if !hideDir {
349+
fmt.Fprintf(w, "\t%s",
350+
dir,
351+
)
352+
}
353+
fmt.Fprint(w, "\n")
354+
276355
}
277356
return w.Flush()
278357
default:

pkg/store/instance_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package store
2+
3+
import (
4+
"bytes"
5+
"os/user"
6+
"path/filepath"
7+
"runtime"
8+
"testing"
9+
10+
"github.com/lima-vm/lima/pkg/limayaml"
11+
"gotest.tools/v3/assert"
12+
)
13+
14+
const separator = string(filepath.Separator)
15+
16+
var vmtype = limayaml.QEMU
17+
var goarch = limayaml.NewArch(runtime.GOARCH)
18+
19+
var instance = Instance{
20+
Name: "foo",
21+
Status: StatusStopped,
22+
VMType: vmtype,
23+
Arch: goarch,
24+
Dir: "dir",
25+
}
26+
27+
var table = "NAME STATUS SSH CPUS MEMORY DISK DIR\n" +
28+
"foo Stopped 127.0.0.1:0 0 0B 0B dir\n"
29+
30+
var tableEmu = "NAME STATUS SSH ARCH CPUS MEMORY DISK DIR\n" +
31+
"foo Stopped 127.0.0.1:0 unknown 0 0B 0B dir\n"
32+
33+
var tableHome = "NAME STATUS SSH CPUS MEMORY DISK DIR\n" +
34+
"foo Stopped 127.0.0.1:0 0 0B 0B ~" + separator + "dir\n"
35+
36+
var tableAll = "NAME STATUS SSH VMTYPE ARCH CPUS MEMORY DISK DIR\n" +
37+
"foo Stopped 127.0.0.1:0 " + vmtype + " " + goarch + " 0 0B 0B dir\n"
38+
39+
// for width 60, everything is hidden
40+
var table60 = "NAME STATUS SSH CPUS MEMORY DISK\n" +
41+
"foo Stopped 127.0.0.1:0 0 0B 0B\n"
42+
43+
// for width 80, identical is hidden (type/arch)
44+
var table80i = "NAME STATUS SSH CPUS MEMORY DISK DIR\n" +
45+
"foo Stopped 127.0.0.1:0 0 0B 0B dir\n"
46+
47+
// for width 80, different arch is still shown (not dir)
48+
var table80d = "NAME STATUS SSH ARCH CPUS MEMORY DISK\n" +
49+
"foo Stopped 127.0.0.1:0 unknown 0 0B 0B\n"
50+
51+
// for width 100, nothing is hidden
52+
var table100 = "NAME STATUS SSH VMTYPE ARCH CPUS MEMORY DISK DIR\n" +
53+
"foo Stopped 127.0.0.1:0 " + vmtype + " " + goarch + " 0 0B 0B dir\n"
54+
55+
// for width 80, directory is hidden (if not identical)
56+
var tableTwo = "NAME STATUS SSH VMTYPE ARCH CPUS MEMORY DISK\n" +
57+
"foo Stopped 127.0.0.1:0 qemu x86_64 0 0B 0B\n" +
58+
"bar Stopped 127.0.0.1:0 vz aarch64 0 0B 0B\n"
59+
60+
func TestPrintInstanceTable(t *testing.T) {
61+
var buf bytes.Buffer
62+
instances := []*Instance{&instance}
63+
PrintInstances(&buf, instances, "table", nil)
64+
assert.Equal(t, table, buf.String())
65+
}
66+
67+
func TestPrintInstanceTableEmu(t *testing.T) {
68+
var buf bytes.Buffer
69+
instance1 := instance
70+
instance1.Arch = "unknown"
71+
instances := []*Instance{&instance1}
72+
PrintInstances(&buf, instances, "table", nil)
73+
assert.Equal(t, tableEmu, buf.String())
74+
}
75+
76+
func TestPrintInstanceTableHome(t *testing.T) {
77+
var buf bytes.Buffer
78+
u, err := user.Current()
79+
assert.NilError(t, err)
80+
instance1 := instance
81+
instance1.Dir = filepath.Join(u.HomeDir, "dir")
82+
instances := []*Instance{&instance1}
83+
PrintInstances(&buf, instances, "table", nil)
84+
assert.Equal(t, tableHome, buf.String())
85+
}
86+
87+
func TestPrintInstanceTable60(t *testing.T) {
88+
var buf bytes.Buffer
89+
instances := []*Instance{&instance}
90+
options := PrintOptions{TerminalWidth: 60}
91+
PrintInstances(&buf, instances, "table", &options)
92+
assert.Equal(t, table60, buf.String())
93+
}
94+
95+
func TestPrintInstanceTable80SameArch(t *testing.T) {
96+
var buf bytes.Buffer
97+
instances := []*Instance{&instance}
98+
options := PrintOptions{TerminalWidth: 80}
99+
PrintInstances(&buf, instances, "table", &options)
100+
assert.Equal(t, table80i, buf.String())
101+
}
102+
103+
func TestPrintInstanceTable80DiffArch(t *testing.T) {
104+
var buf bytes.Buffer
105+
instance1 := instance
106+
instance1.Arch = limayaml.NewArch("unknown")
107+
instances := []*Instance{&instance1}
108+
options := PrintOptions{TerminalWidth: 80}
109+
PrintInstances(&buf, instances, "table", &options)
110+
assert.Equal(t, table80d, buf.String())
111+
}
112+
113+
func TestPrintInstanceTable100(t *testing.T) {
114+
var buf bytes.Buffer
115+
instances := []*Instance{&instance}
116+
options := PrintOptions{TerminalWidth: 100}
117+
PrintInstances(&buf, instances, "table", &options)
118+
assert.Equal(t, table100, buf.String())
119+
}
120+
121+
func TestPrintInstanceTableAll(t *testing.T) {
122+
var buf bytes.Buffer
123+
instances := []*Instance{&instance}
124+
options := PrintOptions{TerminalWidth: 40, AllFields: true}
125+
PrintInstances(&buf, instances, "table", &options)
126+
assert.Equal(t, tableAll, buf.String())
127+
}
128+
129+
func TestPrintInstanceTableTwo(t *testing.T) {
130+
var buf bytes.Buffer
131+
instance1 := instance
132+
instance1.Name = "foo"
133+
instance1.VMType = limayaml.QEMU
134+
instance1.Arch = limayaml.X8664
135+
instance2 := instance
136+
instance2.Name = "bar"
137+
instance2.VMType = limayaml.VZ
138+
instance2.Arch = limayaml.AARCH64
139+
instances := []*Instance{&instance1, &instance2}
140+
options := PrintOptions{TerminalWidth: 80}
141+
PrintInstances(&buf, instances, "table", &options)
142+
assert.Equal(t, tableTwo, buf.String())
143+
}

0 commit comments

Comments
 (0)