first take at layer multiselect

This commit is contained in:
Alex Goodman 2018-06-24 15:26:56 -04:00
parent 02418eaad3
commit 08fd01072c
No known key found for this signature in database
GPG Key ID: 05328C611D8A520E
8 changed files with 76 additions and 43 deletions

View File

@ -1,14 +1,13 @@
package filetree package filetree
import ( import (
"archive/tar"
"sort" "sort"
"strings" "strings"
"github.com/fatih/color" "github.com/fatih/color"
"fmt" "fmt"
"github.com/phayes/permbits" "github.com/phayes/permbits"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/wagoodman/docker-image-explorer/_vendor-20180604210951/github.com/Microsoft/go-winio/archive/tar"
) )
const ( const (

View File

@ -217,9 +217,9 @@ func (tree *FileTree) MarkRemoved(path string) error {
return node.AssignDiffType(Removed) return node.AssignDiffType(Removed)
} }
func StackRange(trees []*FileTree, index int) *FileTree { func StackRange(trees []*FileTree, start, stop int) *FileTree {
tree := trees[0].Copy() tree := trees[0].Copy()
for idx := 0; idx <= index; idx++ { for idx := start; idx <= stop; idx++ {
tree.Stack(trees[idx]) tree.Stack(trees[idx])
} }
return tree return tree

View File

@ -423,7 +423,7 @@ func TestStackRange(t *testing.T) {
upperTree.AddPath(value, fakeData) upperTree.AddPath(value, fakeData)
} }
trees := []*FileTree{lowerTree, upperTree, tree} trees := []*FileTree{lowerTree, upperTree, tree}
StackRange(trees, 2) StackRange(trees, 0, 2)
} }

View File

@ -55,12 +55,17 @@ type Layer struct {
History types.ImageHistory History types.ImageHistory
} }
func (layer *Layer) String() string { func (layer *Layer) Id() string {
id := layer.History.ID[0:25] id := layer.History.ID[0:25]
if len(layer.History.Tags) > 0 { if len(layer.History.Tags) > 0 {
id = "[" + strings.Join(layer.History.Tags, ",") + "]" id = "[" + strings.Join(layer.History.Tags, ",") + "]"
} }
return fmt.Sprintf(LayerFormat, id, humanize.Bytes(uint64(layer.History.Size)), layer.History.CreatedBy) return id
}
func (layer *Layer) String() string {
return fmt.Sprintf(LayerFormat, layer.Id(), humanize.Bytes(uint64(layer.History.Size)), strings.TrimPrefix(layer.History.CreatedBy, "/bin/sh -c "))
} }
func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) { func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {

View File

@ -6,21 +6,30 @@ import (
"github.com/jroimartin/gocui" "github.com/jroimartin/gocui"
"github.com/wagoodman/docker-image-explorer/filetree" "github.com/wagoodman/docker-image-explorer/filetree"
"github.com/fatih/color"
"strings" "strings"
"github.com/lunixbochs/vtclean" "github.com/lunixbochs/vtclean"
) )
const (
CompareLayer CompareType = iota
CompareAll
)
type CompareType int
type FileTreeView struct { type FileTreeView struct {
Name string Name string
gui *gocui.Gui gui *gocui.Gui
view *gocui.View view *gocui.View
header *gocui.View header *gocui.View
TreeIndex int ModelTree *filetree.FileTree
ModelTree *filetree.FileTree ViewTree *filetree.FileTree
ViewTree *filetree.FileTree RefTrees []*filetree.FileTree
RefTrees []*filetree.FileTree HiddenDiffTypes []bool
HiddenDiffTypes []bool CompareMode CompareType
CompareStartIndex int
CompareStopIndex int
} }
func NewFileTreeView(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree) (treeview *FileTreeView) { func NewFileTreeView(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree) (treeview *FileTreeView) {
@ -32,6 +41,7 @@ func NewFileTreeView(name string, gui *gocui.Gui, tree *filetree.FileTree, refTr
treeview.ModelTree = tree treeview.ModelTree = tree
treeview.RefTrees = refTrees treeview.RefTrees = refTrees
treeview.HiddenDiffTypes = make([]bool, 4) treeview.HiddenDiffTypes = make([]bool, 4)
treeview.CompareMode = CompareLayer
return treeview return treeview
} }
@ -88,8 +98,9 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
if layerIndex > len(view.RefTrees)-1 { if layerIndex > len(view.RefTrees)-1 {
return errors.New(fmt.Sprintf("Invalid layer index given: %d of %d", layerIndex, len(view.RefTrees)-1)) return errors.New(fmt.Sprintf("Invalid layer index given: %d of %d", layerIndex, len(view.RefTrees)-1))
} }
newTree := filetree.StackRange(view.RefTrees, layerIndex-1) view.CompareStopIndex = layerIndex
newTree.Compare(view.RefTrees[layerIndex]) newTree := filetree.StackRange(view.RefTrees, view.CompareStartIndex, view.CompareStopIndex-1)
newTree.Compare(view.RefTrees[view.CompareStopIndex])
// preserve view state on copy // preserve view state on copy
visitor := func(node *filetree.FileNode) error { visitor := func(node *filetree.FileNode) error {
@ -104,11 +115,11 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
if debug { if debug {
v, _ := view.gui.View("debug") v, _ := view.gui.View("debug")
v.Clear() v.Clear()
_, _ = fmt.Fprintln(v, view.RefTrees[layerIndex]) _, _ = fmt.Fprintln(v, view.RefTrees[view.CompareStopIndex])
} }
view.view.SetCursor(0, 0) view.view.SetCursor(0, 0)
view.TreeIndex = 0 view.CompareStopIndex = 0
view.ModelTree = newTree view.ModelTree = newTree
view.updateViewTree() view.updateViewTree()
return view.Render() return view.Render()
@ -119,16 +130,16 @@ func (view *FileTreeView) CursorDown() error {
// to let us know what is a valid bounds (i.e. when it hits an empty line) // to let us know what is a valid bounds (i.e. when it hits an empty line)
err := CursorDown(view.gui, view.view) err := CursorDown(view.gui, view.view)
if err == nil { if err == nil {
view.TreeIndex++ view.CompareStopIndex++
} }
return view.Render() return view.Render()
} }
func (view *FileTreeView) CursorUp() error { func (view *FileTreeView) CursorUp() error {
if view.TreeIndex > 0 { if view.CompareStopIndex > 0 {
err := CursorUp(view.gui, view.view) err := CursorUp(view.gui, view.view)
if err == nil { if err == nil {
view.TreeIndex-- view.CompareStopIndex--
} }
} }
return view.Render() return view.Render()
@ -140,7 +151,7 @@ func (view *FileTreeView) getAbsPositionNode() (node *filetree.FileNode) {
var dfsCounter int var dfsCounter int
visiter = func(curNode *filetree.FileNode) error { visiter = func(curNode *filetree.FileNode) error {
if dfsCounter == view.TreeIndex { if dfsCounter == view.CompareStopIndex {
node = curNode node = curNode
} }
dfsCounter++ dfsCounter++
@ -170,7 +181,7 @@ func (view *FileTreeView) toggleShowDiffType(diffType filetree.DiffType) error {
view.HiddenDiffTypes[diffType] = !view.HiddenDiffTypes[diffType] view.HiddenDiffTypes[diffType] = !view.HiddenDiffTypes[diffType]
view.view.SetCursor(0, 0) view.view.SetCursor(0, 0)
view.TreeIndex = 0 view.CompareStopIndex = 0
view.updateViewTree() view.updateViewTree()
return view.Render() return view.Render()
} }
@ -193,12 +204,11 @@ func (view *FileTreeView) updateViewTree() {
} }
func (view *FileTreeView) KeyHelp() string { func (view *FileTreeView) KeyHelp() string {
control := color.New(color.Bold).SprintFunc() return Formatting.Control("[Space]") + ": Collapse dir " +
return control("[Space]") + ": Collapse dir " + Formatting.Control("[^A]") + ": Added files " +
control("[^A]") + ": Added files " + Formatting.Control("[^R]") + ": Removed files " +
control("[^R]") + ": Removed files " + Formatting.Control("[^M]") + ": Modified files " +
control("[^M]") + ": Modified files " + Formatting.Control("[^U]") + ": Unmodified files"
control("[^U]") + ": Unmodified files"
} }
func (view *FileTreeView) Render() error { func (view *FileTreeView) Render() error {
@ -207,7 +217,7 @@ func (view *FileTreeView) Render() error {
view.gui.Update(func(g *gocui.Gui) error { view.gui.Update(func(g *gocui.Gui) error {
view.view.Clear() view.view.Clear()
for idx, line := range lines { for idx, line := range lines {
if idx == view.TreeIndex { if idx == view.CompareStopIndex {
fmt.Fprintln(view.view, Formatting.StatusBar(vtclean.Clean(line, false))) fmt.Fprintln(view.view, Formatting.StatusBar(vtclean.Clean(line, false)))
} else { } else {
fmt.Fprintln(view.view, line) fmt.Fprintln(view.view, line)

View File

@ -51,6 +51,13 @@ func (view *LayerView) Setup(v *gocui.View, header *gocui.View) error {
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorUp() }); err != nil { if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorUp() }); err != nil {
return err return err
} }
if err := view.gui.SetKeybinding(view.Name, gocui.KeyCtrlL, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.setCompareMode(CompareLayer) }); err != nil {
return err
}
if err := view.gui.SetKeybinding(view.Name, gocui.KeyCtrlA, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.setCompareMode(CompareAll) }); err != nil {
return err
}
headerStr := fmt.Sprintf(image.LayerFormat, "Image ID", "Size", "Command") headerStr := fmt.Sprintf(image.LayerFormat, "Image ID", "Size", "Command")
fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false))) fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false)))
@ -58,6 +65,12 @@ func (view *LayerView) Setup(v *gocui.View, header *gocui.View) error {
return view.Render() return view.Render()
} }
func (view *LayerView) setCompareMode(compareMode CompareType) error {
Views.Tree.CompareMode = compareMode
view.Render()
return Views.Tree.setLayer(Views.Tree.CompareStopIndex)
}
func (view *LayerView) Render() error { func (view *LayerView) Render() error {
view.gui.Update(func(g *gocui.Gui) error { view.gui.Update(func(g *gocui.Gui) error {
view.view.Clear() view.view.Clear()
@ -65,10 +78,16 @@ func (view *LayerView) Render() error {
layer := view.Layers[revIdx] layer := view.Layers[revIdx]
idx := (len(view.Layers)-1) - revIdx idx := (len(view.Layers)-1) - revIdx
layerStr := layer.String()
if idx == 0 {
// TODO: add size
layerStr = fmt.Sprintf(image.LayerFormat, layer.History.ID[0:25], "", "FROM "+layer.Id())
}
if idx == view.LayerIndex { if idx == view.LayerIndex {
fmt.Fprintln(view.view, Formatting.StatusBar(layer.String())) fmt.Fprintln(view.view, Formatting.StatusBar(layerStr))
} else { } else {
fmt.Fprintln(view.view, layer.String()) fmt.Fprintln(view.view, layerStr)
} }
} }
@ -103,5 +122,6 @@ func (view *LayerView) CursorUp() error {
} }
func (view *LayerView) KeyHelp() string { func (view *LayerView) KeyHelp() string {
return "blerg" return Formatting.Control("[^L]") + ": Layer Changes " +
Formatting.Control("[^A]") + ": All Changes "
} }

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/jroimartin/gocui" "github.com/jroimartin/gocui"
"github.com/fatih/color"
) )
type StatusView struct { type StatusView struct {
@ -53,10 +52,8 @@ func (view *StatusView) CursorUp() error {
} }
func (view *StatusView) KeyHelp() string { func (view *StatusView) KeyHelp() string {
control := color.New(color.Bold).SprintFunc() return Formatting.Control("[^C]") + ": Quit " +
return control("[^C]") + ": Quit " + Formatting.Control("[^Space]") + ": Switch View "
control("[^Space]") + ": Switch View "
} }
func (view *StatusView) Render() error { func (view *StatusView) Render() error {

View File

@ -7,7 +7,7 @@ import (
"github.com/wagoodman/docker-image-explorer/filetree" "github.com/wagoodman/docker-image-explorer/filetree"
"github.com/wagoodman/docker-image-explorer/image" "github.com/wagoodman/docker-image-explorer/image"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/wagoodman/docker-image-explorer/_vendor-20180604210951/github.com/pkg/errors" "errors"
) )
const debug = false const debug = false
@ -15,6 +15,7 @@ const debug = false
var Formatting struct { var Formatting struct {
Header func(...interface{})(string) Header func(...interface{})(string)
StatusBar func(...interface{})(string) StatusBar func(...interface{})(string)
Control func(...interface{})(string)
} }
var Views struct { var Views struct {
@ -168,6 +169,7 @@ func Render() {
func Run(layers []*image.Layer, refTrees []*filetree.FileTree) { func Run(layers []*image.Layer, refTrees []*filetree.FileTree) {
Formatting.StatusBar = color.New(color.ReverseVideo, color.Bold).SprintFunc() Formatting.StatusBar = color.New(color.ReverseVideo, color.Bold).SprintFunc()
Formatting.Header = color.New(color.Bold).SprintFunc() Formatting.Header = color.New(color.Bold).SprintFunc()
Formatting.Control = color.New(color.Bold).SprintFunc()
g, err := gocui.NewGui(gocui.OutputNormal) g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil { if err != nil {
@ -180,7 +182,7 @@ func Run(layers []*image.Layer, refTrees []*filetree.FileTree) {
Views.Layer = NewLayerView("side", g, layers) Views.Layer = NewLayerView("side", g, layers)
Views.lookup[Views.Layer.Name] = Views.Layer Views.lookup[Views.Layer.Name] = Views.Layer
Views.Tree = NewFileTreeView("main", g, filetree.StackRange(refTrees, 0), refTrees) Views.Tree = NewFileTreeView("main", g, filetree.StackRange(refTrees, 0,0), refTrees)
Views.lookup[Views.Tree.Name] = Views.Tree Views.lookup[Views.Tree.Name] = Views.Tree
Views.Status = NewStatusView("status", g) Views.Status = NewStatusView("status", g)