first take at layer multiselect
This commit is contained in:
parent
02418eaad3
commit
08fd01072c
@ -1,14 +1,13 @@
|
||||
package filetree
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"fmt"
|
||||
"github.com/phayes/permbits"
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/wagoodman/docker-image-explorer/_vendor-20180604210951/github.com/Microsoft/go-winio/archive/tar"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -217,9 +217,9 @@ func (tree *FileTree) MarkRemoved(path string) error {
|
||||
return node.AssignDiffType(Removed)
|
||||
}
|
||||
|
||||
func StackRange(trees []*FileTree, index int) *FileTree {
|
||||
func StackRange(trees []*FileTree, start, stop int) *FileTree {
|
||||
tree := trees[0].Copy()
|
||||
for idx := 0; idx <= index; idx++ {
|
||||
for idx := start; idx <= stop; idx++ {
|
||||
tree.Stack(trees[idx])
|
||||
}
|
||||
return tree
|
||||
|
@ -423,7 +423,7 @@ func TestStackRange(t *testing.T) {
|
||||
upperTree.AddPath(value, fakeData)
|
||||
}
|
||||
trees := []*FileTree{lowerTree, upperTree, tree}
|
||||
StackRange(trees, 2)
|
||||
StackRange(trees, 0, 2)
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,12 +55,17 @@ type Layer struct {
|
||||
History types.ImageHistory
|
||||
}
|
||||
|
||||
func (layer *Layer) String() string {
|
||||
func (layer *Layer) Id() string {
|
||||
id := layer.History.ID[0:25]
|
||||
if len(layer.History.Tags) > 0 {
|
||||
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) {
|
||||
|
@ -6,21 +6,30 @@ import (
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/wagoodman/docker-image-explorer/filetree"
|
||||
"github.com/fatih/color"
|
||||
"strings"
|
||||
"github.com/lunixbochs/vtclean"
|
||||
)
|
||||
|
||||
const (
|
||||
CompareLayer CompareType = iota
|
||||
CompareAll
|
||||
)
|
||||
|
||||
type CompareType int
|
||||
|
||||
|
||||
type FileTreeView struct {
|
||||
Name string
|
||||
gui *gocui.Gui
|
||||
view *gocui.View
|
||||
header *gocui.View
|
||||
TreeIndex int
|
||||
ModelTree *filetree.FileTree
|
||||
ViewTree *filetree.FileTree
|
||||
RefTrees []*filetree.FileTree
|
||||
HiddenDiffTypes []bool
|
||||
Name string
|
||||
gui *gocui.Gui
|
||||
view *gocui.View
|
||||
header *gocui.View
|
||||
ModelTree *filetree.FileTree
|
||||
ViewTree *filetree.FileTree
|
||||
RefTrees []*filetree.FileTree
|
||||
HiddenDiffTypes []bool
|
||||
CompareMode CompareType
|
||||
CompareStartIndex int
|
||||
CompareStopIndex int
|
||||
}
|
||||
|
||||
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.RefTrees = refTrees
|
||||
treeview.HiddenDiffTypes = make([]bool, 4)
|
||||
treeview.CompareMode = CompareLayer
|
||||
|
||||
return treeview
|
||||
}
|
||||
@ -88,8 +98,9 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
|
||||
if 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)
|
||||
newTree.Compare(view.RefTrees[layerIndex])
|
||||
view.CompareStopIndex = layerIndex
|
||||
newTree := filetree.StackRange(view.RefTrees, view.CompareStartIndex, view.CompareStopIndex-1)
|
||||
newTree.Compare(view.RefTrees[view.CompareStopIndex])
|
||||
|
||||
// preserve view state on copy
|
||||
visitor := func(node *filetree.FileNode) error {
|
||||
@ -104,11 +115,11 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
|
||||
if debug {
|
||||
v, _ := view.gui.View("debug")
|
||||
v.Clear()
|
||||
_, _ = fmt.Fprintln(v, view.RefTrees[layerIndex])
|
||||
_, _ = fmt.Fprintln(v, view.RefTrees[view.CompareStopIndex])
|
||||
}
|
||||
|
||||
view.view.SetCursor(0, 0)
|
||||
view.TreeIndex = 0
|
||||
view.CompareStopIndex = 0
|
||||
view.ModelTree = newTree
|
||||
view.updateViewTree()
|
||||
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)
|
||||
err := CursorDown(view.gui, view.view)
|
||||
if err == nil {
|
||||
view.TreeIndex++
|
||||
view.CompareStopIndex++
|
||||
}
|
||||
return view.Render()
|
||||
}
|
||||
|
||||
func (view *FileTreeView) CursorUp() error {
|
||||
if view.TreeIndex > 0 {
|
||||
if view.CompareStopIndex > 0 {
|
||||
err := CursorUp(view.gui, view.view)
|
||||
if err == nil {
|
||||
view.TreeIndex--
|
||||
view.CompareStopIndex--
|
||||
}
|
||||
}
|
||||
return view.Render()
|
||||
@ -140,7 +151,7 @@ func (view *FileTreeView) getAbsPositionNode() (node *filetree.FileNode) {
|
||||
var dfsCounter int
|
||||
|
||||
visiter = func(curNode *filetree.FileNode) error {
|
||||
if dfsCounter == view.TreeIndex {
|
||||
if dfsCounter == view.CompareStopIndex {
|
||||
node = curNode
|
||||
}
|
||||
dfsCounter++
|
||||
@ -170,7 +181,7 @@ func (view *FileTreeView) toggleShowDiffType(diffType filetree.DiffType) error {
|
||||
view.HiddenDiffTypes[diffType] = !view.HiddenDiffTypes[diffType]
|
||||
|
||||
view.view.SetCursor(0, 0)
|
||||
view.TreeIndex = 0
|
||||
view.CompareStopIndex = 0
|
||||
view.updateViewTree()
|
||||
return view.Render()
|
||||
}
|
||||
@ -193,12 +204,11 @@ func (view *FileTreeView) updateViewTree() {
|
||||
}
|
||||
|
||||
func (view *FileTreeView) KeyHelp() string {
|
||||
control := color.New(color.Bold).SprintFunc()
|
||||
return control("[Space]") + ": Collapse dir " +
|
||||
control("[^A]") + ": Added files " +
|
||||
control("[^R]") + ": Removed files " +
|
||||
control("[^M]") + ": Modified files " +
|
||||
control("[^U]") + ": Unmodified files"
|
||||
return Formatting.Control("[Space]") + ": Collapse dir " +
|
||||
Formatting.Control("[^A]") + ": Added files " +
|
||||
Formatting.Control("[^R]") + ": Removed files " +
|
||||
Formatting.Control("[^M]") + ": Modified files " +
|
||||
Formatting.Control("[^U]") + ": Unmodified files"
|
||||
}
|
||||
|
||||
func (view *FileTreeView) Render() error {
|
||||
@ -207,7 +217,7 @@ func (view *FileTreeView) Render() error {
|
||||
view.gui.Update(func(g *gocui.Gui) error {
|
||||
view.view.Clear()
|
||||
for idx, line := range lines {
|
||||
if idx == view.TreeIndex {
|
||||
if idx == view.CompareStopIndex {
|
||||
fmt.Fprintln(view.view, Formatting.StatusBar(vtclean.Clean(line, false)))
|
||||
} else {
|
||||
fmt.Fprintln(view.view, line)
|
||||
|
@ -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 {
|
||||
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")
|
||||
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()
|
||||
}
|
||||
|
||||
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 {
|
||||
view.gui.Update(func(g *gocui.Gui) error {
|
||||
view.view.Clear()
|
||||
@ -65,10 +78,16 @@ func (view *LayerView) Render() error {
|
||||
layer := view.Layers[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 {
|
||||
fmt.Fprintln(view.view, Formatting.StatusBar(layer.String()))
|
||||
fmt.Fprintln(view.view, Formatting.StatusBar(layerStr))
|
||||
} 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 {
|
||||
return "blerg"
|
||||
return Formatting.Control("[^L]") + ": Layer Changes " +
|
||||
Formatting.Control("[^A]") + ": All Changes "
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
type StatusView struct {
|
||||
@ -53,10 +52,8 @@ func (view *StatusView) CursorUp() error {
|
||||
}
|
||||
|
||||
func (view *StatusView) KeyHelp() string {
|
||||
control := color.New(color.Bold).SprintFunc()
|
||||
return control("[^C]") + ": Quit " +
|
||||
control("[^Space]") + ": Switch View "
|
||||
|
||||
return Formatting.Control("[^C]") + ": Quit " +
|
||||
Formatting.Control("[^Space]") + ": Switch View "
|
||||
}
|
||||
|
||||
func (view *StatusView) Render() error {
|
||||
|
6
ui/ui.go
6
ui/ui.go
@ -7,7 +7,7 @@ import (
|
||||
"github.com/wagoodman/docker-image-explorer/filetree"
|
||||
"github.com/wagoodman/docker-image-explorer/image"
|
||||
"github.com/fatih/color"
|
||||
"github.com/wagoodman/docker-image-explorer/_vendor-20180604210951/github.com/pkg/errors"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const debug = false
|
||||
@ -15,6 +15,7 @@ const debug = false
|
||||
var Formatting struct {
|
||||
Header func(...interface{})(string)
|
||||
StatusBar func(...interface{})(string)
|
||||
Control func(...interface{})(string)
|
||||
}
|
||||
|
||||
var Views struct {
|
||||
@ -168,6 +169,7 @@ func Render() {
|
||||
func Run(layers []*image.Layer, refTrees []*filetree.FileTree) {
|
||||
Formatting.StatusBar = color.New(color.ReverseVideo, color.Bold).SprintFunc()
|
||||
Formatting.Header = color.New(color.Bold).SprintFunc()
|
||||
Formatting.Control = color.New(color.Bold).SprintFunc()
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
if err != nil {
|
||||
@ -180,7 +182,7 @@ func Run(layers []*image.Layer, refTrees []*filetree.FileTree) {
|
||||
Views.Layer = NewLayerView("side", g, layers)
|
||||
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.Status = NewStatusView("status", g)
|
||||
|
Loading…
x
Reference in New Issue
Block a user