This commit is contained in:
Alex Goodman 2018-10-16 23:37:19 -04:00
parent 8ca96849da
commit e700ec3745
No known key found for this signature in database
GPG Key ID: 05328C611D8A520E
18 changed files with 154 additions and 161 deletions

View File

@ -1,12 +1,12 @@
package cmd package cmd
import ( import (
"github.com/spf13/cobra"
"fmt" "fmt"
"os" "github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/wagoodman/dive/image" "github.com/wagoodman/dive/image"
"github.com/wagoodman/dive/ui" "github.com/wagoodman/dive/ui"
"github.com/fatih/color" "os"
) )
// analyze takes a docker image tag, digest, or id and displayes the // analyze takes a docker image tag, digest, or id and displayes the

View File

@ -1,14 +1,14 @@
package cmd package cmd
import ( import (
"github.com/spf13/cobra"
"os/exec"
"os"
"strings"
"io/ioutil"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/wagoodman/dive/image" "github.com/wagoodman/dive/image"
"github.com/wagoodman/dive/ui" "github.com/wagoodman/dive/ui"
"io/ioutil"
"os"
"os/exec"
"strings"
) )
// buildCmd represents the build command // buildCmd represents the build command
@ -48,7 +48,7 @@ func doBuild(cmd *cobra.Command, args []string) {
} }
// runDockerCmd runs a given Docker command in the current tty // runDockerCmd runs a given Docker command in the current tty
func runDockerCmd(cmdStr string, args... string) error { func runDockerCmd(cmdStr string, args ...string) error {
allArgs := cleanArgs(append([]string{cmdStr}, args...)) allArgs := cleanArgs(append([]string{cmdStr}, args...))

View File

@ -2,11 +2,11 @@ package cmd
import ( import (
"fmt" "fmt"
"os"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
log "github.com/sirupsen/logrus" "os"
) )
var cfgFile string var cfgFile string
@ -73,7 +73,7 @@ func initLogging() {
// TODO: clean this up and make more configurable // TODO: clean this up and make more configurable
var filename string = "dive.log" var filename string = "dive.log"
// create the log file if doesn't exist. And append to it if it already exists. // create the log file if doesn't exist. And append to it if it already exists.
f, err := os.OpenFile(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE, 0644) f, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
Formatter := new(log.TextFormatter) Formatter := new(log.TextFormatter)
Formatter.DisableTimestamp = true Formatter.DisableTimestamp = true
log.SetFormatter(Formatter) log.SetFormatter(Formatter)
@ -81,7 +81,7 @@ func initLogging() {
if err != nil { if err != nil {
// cannot open log file. Logging to stderr // cannot open log file. Logging to stderr
fmt.Println(err) fmt.Println(err)
}else{ } else {
log.SetOutput(f) log.SetOutput(f)
} }
log.Debug("Starting Dive...") log.Debug("Starting Dive...")

View File

@ -40,7 +40,7 @@ type FileInfo struct {
type DiffType int type DiffType int
// NewNodeData creates an empty NodeData struct for a FileNode // NewNodeData creates an empty NodeData struct for a FileNode
func NewNodeData() (*NodeData) { func NewNodeData() *NodeData {
return &NodeData{ return &NodeData{
ViewInfo: *NewViewInfo(), ViewInfo: *NewViewInfo(),
FileInfo: FileInfo{}, FileInfo: FileInfo{},
@ -49,7 +49,7 @@ func NewNodeData() (*NodeData) {
} }
// Copy duplicates a NodeData // Copy duplicates a NodeData
func (data *NodeData) Copy() (*NodeData) { func (data *NodeData) Copy() *NodeData {
return &NodeData{ return &NodeData{
ViewInfo: *data.ViewInfo.Copy(), ViewInfo: *data.ViewInfo.Copy(),
FileInfo: *data.FileInfo.Copy(), FileInfo: *data.FileInfo.Copy(),
@ -57,7 +57,6 @@ func (data *NodeData) Copy() (*NodeData) {
} }
} }
// NewViewInfo creates a default ViewInfo // NewViewInfo creates a default ViewInfo
func NewViewInfo() (view *ViewInfo) { func NewViewInfo() (view *ViewInfo) {
return &ViewInfo{ return &ViewInfo{
@ -120,7 +119,6 @@ func (data *FileInfo) Compare(other FileInfo) DiffType {
return Changed return Changed
} }
// String of a DiffType // String of a DiffType
func (diff DiffType) String() string { func (diff DiffType) String() string {
switch diff { switch diff {
@ -145,5 +143,3 @@ func (diff DiffType) merge(other DiffType) DiffType {
} }
return Changed return Changed
} }

View File

@ -42,7 +42,7 @@ func Efficiency(trees []*FileTree) (float64, EfficiencySlice) {
if _, ok := efficiencyMap[path]; !ok { if _, ok := efficiencyMap[path]; !ok {
efficiencyMap[path] = &EfficiencyData{ efficiencyMap[path] = &EfficiencyData{
Path: path, Path: path,
Nodes: make([]*FileNode,0), Nodes: make([]*FileNode, 0),
minDiscoveredSize: -1, minDiscoveredSize: -1,
} }
} }
@ -66,7 +66,6 @@ func Efficiency(trees []*FileTree) (float64, EfficiencySlice) {
tree.VisitDepthChildFirst(visitor, visitEvaluator) tree.VisitDepthChildFirst(visitor, visitEvaluator)
} }
// calculate the score // calculate the score
var minimumPathSizes int64 var minimumPathSizes int64
var discoveredPathSizes int64 var discoveredPathSizes int64
@ -81,5 +80,3 @@ func Efficiency(trees []*FileTree) (float64, EfficiencySlice) {
return score, inefficientMatches return score, inefficientMatches
} }

View File

@ -47,4 +47,3 @@ package filetree
// t.Fatalf("Expected %f but got %f", expected, actual) // t.Fatalf("Expected %f but got %f", expected, actual)
// } // }
// } // }

View File

@ -15,7 +15,7 @@ const (
AttributeFormat = "%s%s %10s %10s " AttributeFormat = "%s%s %10s %10s "
) )
var diffTypeColor = map[DiffType]*color.Color { var diffTypeColor = map[DiffType]*color.Color{
Added: color.New(color.FgGreen), Added: color.New(color.FgGreen),
Removed: color.New(color.FgRed), Removed: color.New(color.FgRed),
Changed: color.New(color.FgYellow), Changed: color.New(color.FgYellow),

View File

@ -44,7 +44,6 @@ func TestAddChild(t *testing.T) {
t.Errorf("Expected 'ones' payload to be %+v got %+v.", expectedFC, actualFC) t.Errorf("Expected 'ones' payload to be %+v got %+v.", expectedFC, actualFC)
} }
} }
func TestRemoveChild(t *testing.T) { func TestRemoveChild(t *testing.T) {

View File

@ -2,9 +2,9 @@ package filetree
import ( import (
"fmt" "fmt"
"strings"
"github.com/satori/go.uuid" "github.com/satori/go.uuid"
"sort" "sort"
"strings"
) )
const ( const (
@ -40,7 +40,7 @@ func NewFileTree() (tree *FileTree) {
// renderParams is a representation of a FileNode in the context of the greater tree. All // renderParams is a representation of a FileNode in the context of the greater tree. All
// data stored is necessary for rendering a single line in a tree format. // data stored is necessary for rendering a single line in a tree format.
type renderParams struct{ type renderParams struct {
node *FileNode node *FileNode
spaces []bool spaces []bool
childSpaces []bool childSpaces []bool
@ -52,11 +52,11 @@ type renderParams struct{
// is rendered on its own line, the returned string shows the visible nodes not affected by a collapsed parent. // is rendered on its own line, the returned string shows the visible nodes not affected by a collapsed parent.
func (tree *FileTree) renderStringTreeBetween(startRow, stopRow int, showAttributes bool) string { func (tree *FileTree) renderStringTreeBetween(startRow, stopRow int, showAttributes bool) string {
// generate a list of nodes to render // generate a list of nodes to render
var params = make([]renderParams,0) var params = make([]renderParams, 0)
var result string var result string
// visit from the front of the list // visit from the front of the list
var paramsToVisit = []renderParams{ {node: tree.Root, spaces: []bool{}, showCollapsed: false, isLast: false} } var paramsToVisit = []renderParams{{node: tree.Root, spaces: []bool{}, showCollapsed: false, isLast: false}}
for currentRow := 0; len(paramsToVisit) > 0 && currentRow <= stopRow; currentRow++ { for currentRow := 0; len(paramsToVisit) > 0 && currentRow <= stopRow; currentRow++ {
// pop the first node // pop the first node
var currentParams renderParams var currentParams renderParams
@ -70,7 +70,7 @@ func (tree *FileTree) renderStringTreeBetween(startRow, stopRow int, showAttribu
// we should always visit nodes in order // we should always visit nodes in order
sort.Strings(keys) sort.Strings(keys)
var childParams = make([]renderParams,0) var childParams = make([]renderParams, 0)
for idx, name := range keys { for idx, name := range keys {
child := currentParams.node.Children[name] child := currentParams.node.Children[name]
// don't visit this node... // don't visit this node...

View File

@ -13,10 +13,10 @@ import (
"strings" "strings"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/wagoodman/dive/filetree"
"golang.org/x/net/context"
"github.com/wagoodman/jotframe"
"github.com/k0kubun/go-ansi" "github.com/k0kubun/go-ansi"
"github.com/wagoodman/dive/filetree"
"github.com/wagoodman/jotframe"
"golang.org/x/net/context"
) )
// TODO: this file should be rethought... but since it's only for preprocessing it'll be tech debt for now. // TODO: this file should be rethought... but since it's only for preprocessing it'll be tech debt for now.
@ -33,7 +33,7 @@ type ProgressBar struct {
rawCurrent int64 rawCurrent int64
} }
func NewProgressBar(total int64) *ProgressBar{ func NewProgressBar(total int64) *ProgressBar {
return &ProgressBar{ return &ProgressBar{
rawTotal: total, rawTotal: total,
} }
@ -46,7 +46,7 @@ func (pb *ProgressBar) Done() {
func (pb *ProgressBar) Update(currentValue int64) (hasChanged bool) { func (pb *ProgressBar) Update(currentValue int64) (hasChanged bool) {
pb.rawCurrent = currentValue pb.rawCurrent = currentValue
percent := int(100.0*(float64(pb.rawCurrent) / float64(pb.rawTotal))) percent := int(100.0 * (float64(pb.rawCurrent) / float64(pb.rawTotal)))
if percent != pb.percent { if percent != pb.percent {
hasChanged = true hasChanged = true
} }
@ -56,7 +56,7 @@ func (pb *ProgressBar) Update(currentValue int64) (hasChanged bool) {
func (pb *ProgressBar) String() string { func (pb *ProgressBar) String() string {
width := 40 width := 40
done := int((pb.percent*width)/100.0) done := int((pb.percent * width) / 100.0)
todo := width - done todo := width - done
head := 1 head := 1
// if pb.percent >= 100 { // if pb.percent >= 100 {
@ -132,7 +132,7 @@ func NewImageConfig(reader *tar.Reader, header *tar.Header) ImageConfig {
return imageConfig return imageConfig
} }
func GetImageConfig(imageTarPath string, manifest ImageManifest) ImageConfig{ func GetImageConfig(imageTarPath string, manifest ImageManifest) ImageConfig {
var config ImageConfig var config ImageConfig
// read through the image contents and build a tree // read through the image contents and build a tree
fmt.Println(" Fetching image config...") fmt.Println(" Fetching image config...")
@ -239,7 +239,7 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
} }
observedBytes += header.Size observedBytes += header.Size
percent = int(100.0*(float64(observedBytes) / float64(totalSize))) percent = int(100.0 * (float64(observedBytes) / float64(totalSize)))
io.WriteString(frame.Header(), fmt.Sprintf(" Discovering layers... %d %%", percent)) io.WriteString(frame.Header(), fmt.Sprintf(" Discovering layers... %d %%", percent))
name := header.Name name := header.Name
@ -254,7 +254,7 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
panic(err) panic(err)
} }
shortName := name[:15] shortName := name[:15]
io.WriteString(line, " ├─ " + shortName + " : loading...") io.WriteString(line, " ├─ "+shortName+" : loading...")
var tarredBytes = make([]byte, header.Size) var tarredBytes = make([]byte, header.Size)
@ -263,7 +263,6 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
panic(err) panic(err)
} }
go processLayerTar(line, layerMap, name, tarredBytes) go processLayerTar(line, layerMap, name, tarredBytes)
} else if name == "manifest.json" { } else if name == "manifest.json" {
@ -292,7 +291,7 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
// note that the image config stores images in reverse chronological order, so iterate backwards through layers // note that the image config stores images in reverse chronological order, so iterate backwards through layers
// as you iterate chronologically through history (ignoring history items that have no layer contents) // as you iterate chronologically through history (ignoring history items that have no layer contents)
layerIdx := len(trees)-1 layerIdx := len(trees) - 1
for idx := 0; idx < len(config.History); idx++ { for idx := 0; idx < len(config.History); idx++ {
// ignore empty layers, we are only observing layers with content // ignore empty layers, we are only observing layers with content
if config.History[idx].EmptyLayer { if config.History[idx].EmptyLayer {

View File

@ -1,10 +1,10 @@
package image package image
import ( import (
"github.com/wagoodman/dive/filetree"
"strings"
"fmt" "fmt"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/wagoodman/dive/filetree"
"strings"
) )
const ( const (
@ -44,4 +44,3 @@ func (layer *Layer) String() string {
humanize.Bytes(uint64(layer.History.Size)), humanize.Bytes(uint64(layer.History.Size)),
strings.TrimPrefix(layer.History.CreatedBy, "/bin/sh -c ")) strings.TrimPrefix(layer.History.CreatedBy, "/bin/sh -c "))
} }

View File

@ -33,4 +33,3 @@ var (
func main() { func main() {
cmd.Execute() cmd.Execute()
} }

View File

@ -3,12 +3,12 @@ package ui
import ( import (
"fmt" "fmt"
"github.com/dustin/go-humanize"
"github.com/jroimartin/gocui" "github.com/jroimartin/gocui"
"github.com/lunixbochs/vtclean" "github.com/lunixbochs/vtclean"
"strings"
"github.com/wagoodman/dive/filetree" "github.com/wagoodman/dive/filetree"
"strconv" "strconv"
"github.com/dustin/go-humanize" "strings"
) )
// DetailsView holds the UI objects and data models for populating the lower-left pane. Specifically the pane that // DetailsView holds the UI objects and data models for populating the lower-left pane. Specifically the pane that
@ -61,7 +61,9 @@ func (view *DetailsView) Setup(v *gocui.View, header *gocui.View) error {
// IsVisible indicates if the details view pane is currently initialized. // IsVisible indicates if the details view pane is currently initialized.
func (view *DetailsView) IsVisible() bool { func (view *DetailsView) IsVisible() bool {
if view == nil {return false} if view == nil {
return false
}
return true return true
} }
@ -95,7 +97,7 @@ func (view *DetailsView) Render() error {
template := "%5s %12s %-s\n" template := "%5s %12s %-s\n"
var trueInefficiencies int var trueInefficiencies int
inefficiencyReport := fmt.Sprintf(Formatting.Header(template), "Count", "Total Space", "Path") inefficiencyReport := fmt.Sprintf(Formatting.Header(template), "Count", "Total Space", "Path")
for idx := len(view.inefficiencies)-1; idx > 0; idx-- { for idx := len(view.inefficiencies) - 1; idx > 0; idx-- {
data := view.inefficiencies[idx] data := view.inefficiencies[idx]
if data.CumulativeSize == 0 { if data.CumulativeSize == 0 {
continue continue
@ -115,7 +117,7 @@ func (view *DetailsView) Render() error {
// update header // update header
view.header.Clear() view.header.Clear()
width, _ := g.Size() width, _ := g.Size()
headerStr := fmt.Sprintf("[Image & Layer Details]%s", strings.Repeat("─",width*2)) headerStr := fmt.Sprintf("[Image & Layer Details]%s", strings.Repeat("─", width*2))
fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false))) fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false)))
// update contents // update contents

View File

@ -6,8 +6,8 @@ import (
"strings" "strings"
"github.com/jroimartin/gocui" "github.com/jroimartin/gocui"
"github.com/wagoodman/dive/filetree"
"github.com/lunixbochs/vtclean" "github.com/lunixbochs/vtclean"
"github.com/wagoodman/dive/filetree"
) )
const ( const (
@ -102,7 +102,9 @@ func (view *FileTreeView) height() uint {
// IsVisible indicates if the file tree view pane is currently initialized // IsVisible indicates if the file tree view pane is currently initialized
func (view *FileTreeView) IsVisible() bool { func (view *FileTreeView) IsVisible() bool {
if view == nil {return false} if view == nil {
return false
}
return true return true
} }
@ -178,7 +180,6 @@ func (view *FileTreeView) CursorDown() error {
return view.Render() return view.Render()
} }
// CursorUp moves the cursor up and renders the view. // CursorUp moves the cursor up and renders the view.
// Note: we cannot use the gocui buffer since any state change requires writing the entire tree to the buffer. // Note: we cannot use the gocui buffer since any state change requires writing the entire tree to the buffer.
// Instead we are keeping an upper and lower bounds of the tree string to render and only flushing // Instead we are keeping an upper and lower bounds of the tree string to render and only flushing
@ -303,7 +304,7 @@ func (view *FileTreeView) Update() error {
// Render flushes the state objects (file tree) to the pane. // Render flushes the state objects (file tree) to the pane.
func (view *FileTreeView) Render() error { func (view *FileTreeView) Render() error {
treeString := view.ViewTree.StringBetween(view.bufferIndexLowerBound, view.bufferIndexUpperBound,true) treeString := view.ViewTree.StringBetween(view.bufferIndexLowerBound, view.bufferIndexUpperBound, true)
lines := strings.Split(treeString, "\n") lines := strings.Split(treeString, "\n")
// undo a cursor down that has gone past bottom of the visible tree // undo a cursor down that has gone past bottom of the visible tree
@ -318,7 +319,7 @@ func (view *FileTreeView) Render() error {
// indicate when selected // indicate when selected
if view.gui.CurrentView() == view.view { if view.gui.CurrentView() == view.view {
title = "● "+title title = "● " + title
} }
view.gui.Update(func(g *gocui.Gui) error { view.gui.Update(func(g *gocui.Gui) error {
@ -346,9 +347,9 @@ func (view *FileTreeView) Render() error {
// KeyHelp indicates all the possible actions a user can take while the current pane is selected. // KeyHelp indicates all the possible actions a user can take while the current pane is selected.
func (view *FileTreeView) KeyHelp() string { func (view *FileTreeView) KeyHelp() string {
return renderStatusOption("Space","Collapse dir", false) + return renderStatusOption("Space", "Collapse dir", false) +
renderStatusOption("^A","Added files", !view.HiddenDiffTypes[filetree.Added]) + renderStatusOption("^A", "Added files", !view.HiddenDiffTypes[filetree.Added]) +
renderStatusOption("^R","Removed files", !view.HiddenDiffTypes[filetree.Removed]) + renderStatusOption("^R", "Removed files", !view.HiddenDiffTypes[filetree.Removed]) +
renderStatusOption("^M","Modified files", !view.HiddenDiffTypes[filetree.Changed]) + renderStatusOption("^M", "Modified files", !view.HiddenDiffTypes[filetree.Changed]) +
renderStatusOption("^U","Unmodified files", !view.HiddenDiffTypes[filetree.Unchanged]) renderStatusOption("^U", "Unmodified files", !view.HiddenDiffTypes[filetree.Unchanged])
} }

View File

@ -55,7 +55,9 @@ func (view *FilterView) Setup(v *gocui.View, header *gocui.View) error {
// IsVisible indicates if the filter view pane is currently initialized // IsVisible indicates if the filter view pane is currently initialized
func (view *FilterView) IsVisible() bool { func (view *FilterView) IsVisible() bool {
if view == nil {return false} if view == nil {
return false
}
return !view.hidden return !view.hidden
} }

View File

@ -3,10 +3,10 @@ package ui
import ( import (
"fmt" "fmt"
"github.com/jroimartin/gocui"
"github.com/wagoodman/dive/image"
"github.com/lunixbochs/vtclean"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/jroimartin/gocui"
"github.com/lunixbochs/vtclean"
"github.com/wagoodman/dive/image"
"strings" "strings"
) )
@ -69,7 +69,9 @@ func (view *LayerView) Setup(v *gocui.View, header *gocui.View) error {
// IsVisible indicates if the layer view pane is currently initialized. // IsVisible indicates if the layer view pane is currently initialized.
func (view *LayerView) IsVisible() bool { func (view *LayerView) IsVisible() bool {
if view == nil {return false} if view == nil {
return false
}
return true return true
} }
@ -78,7 +80,7 @@ func (view *LayerView) CursorDown() error {
if view.LayerIndex < len(view.Layers) { if view.LayerIndex < len(view.Layers) {
err := CursorDown(view.gui, view.view) err := CursorDown(view.gui, view.view)
if err == nil { if err == nil {
view.SetCursor(view.LayerIndex+1) view.SetCursor(view.LayerIndex + 1)
} }
} }
return nil return nil
@ -89,7 +91,7 @@ func (view *LayerView) CursorUp() error {
if view.LayerIndex > 0 { if view.LayerIndex > 0 {
err := CursorUp(view.gui, view.view) err := CursorUp(view.gui, view.view)
if err == nil { if err == nil {
view.SetCursor(view.LayerIndex-1) view.SetCursor(view.LayerIndex - 1)
} }
} }
return nil return nil
@ -127,11 +129,11 @@ func (view *LayerView) getCompareIndexes() (bottomTreeStart, bottomTreeStop, top
bottomTreeStop = view.LayerIndex bottomTreeStop = view.LayerIndex
topTreeStart = view.LayerIndex topTreeStart = view.LayerIndex
} else if view.CompareMode == CompareLayer { } else if view.CompareMode == CompareLayer {
bottomTreeStop = view.LayerIndex -1 bottomTreeStop = view.LayerIndex - 1
topTreeStart = view.LayerIndex topTreeStart = view.LayerIndex
} else { } else {
bottomTreeStop = view.CompareStartIndex bottomTreeStop = view.CompareStartIndex
topTreeStart = view.CompareStartIndex+1 topTreeStart = view.CompareStartIndex + 1
} }
return bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop return bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop
@ -165,7 +167,7 @@ func (view *LayerView) Render() error {
// indicate when selected // indicate when selected
title := "Layers" title := "Layers"
if view.gui.CurrentView() == view.view { if view.gui.CurrentView() == view.view {
title = "● "+title title = "● " + title
} }
view.gui.Update(func(g *gocui.Gui) error { view.gui.Update(func(g *gocui.Gui) error {
@ -180,7 +182,7 @@ func (view *LayerView) Render() error {
view.view.Clear() view.view.Clear()
for revIdx := len(view.Layers) - 1; revIdx >= 0; revIdx-- { for revIdx := len(view.Layers) - 1; revIdx >= 0; revIdx-- {
layer := view.Layers[revIdx] layer := view.Layers[revIdx]
idx := (len(view.Layers)-1) - revIdx idx := (len(view.Layers) - 1) - revIdx
layerStr := layer.String() layerStr := layer.String()
if idx == 0 { if idx == 0 {
@ -197,9 +199,9 @@ func (view *LayerView) Render() error {
compareBar := view.renderCompareBar(idx) compareBar := view.renderCompareBar(idx)
if idx == view.LayerIndex { if idx == view.LayerIndex {
fmt.Fprintln(view.view, compareBar + " " + Formatting.Selected(layerStr)) fmt.Fprintln(view.view, compareBar+" "+Formatting.Selected(layerStr))
} else { } else {
fmt.Fprintln(view.view, compareBar + " " + layerStr) fmt.Fprintln(view.view, compareBar+" "+layerStr)
} }
} }
@ -208,9 +210,8 @@ func (view *LayerView) Render() error {
return nil return nil
} }
// KeyHelp indicates all the possible actions a user can take while the current pane is selected. // KeyHelp indicates all the possible actions a user can take while the current pane is selected.
func (view *LayerView) KeyHelp() string { func (view *LayerView) KeyHelp() string {
return renderStatusOption("^L","Show layer changes", view.CompareMode == CompareLayer) + return renderStatusOption("^L", "Show layer changes", view.CompareMode == CompareLayer) +
renderStatusOption("^A","Show aggregated changes", view.CompareMode == CompareAll) renderStatusOption("^A", "Show aggregated changes", view.CompareMode == CompareAll)
} }

View File

@ -40,7 +40,9 @@ func (view *StatusView) Setup(v *gocui.View, header *gocui.View) error {
// IsVisible indicates if the status view pane is currently initialized. // IsVisible indicates if the status view pane is currently initialized.
func (view *StatusView) IsVisible() bool { func (view *StatusView) IsVisible() bool {
if view == nil {return false} if view == nil {
return false
}
return true return true
} }
@ -63,7 +65,7 @@ func (view *StatusView) Update() error {
func (view *StatusView) Render() error { func (view *StatusView) Render() error {
view.gui.Update(func(g *gocui.Gui) error { view.gui.Update(func(g *gocui.Gui) error {
view.view.Clear() view.view.Clear()
fmt.Fprintln(view.view, view.KeyHelp()+Views.lookup[view.gui.CurrentView().Name()].KeyHelp() + Formatting.StatusNormal("▏" + strings.Repeat(" ", 1000))) fmt.Fprintln(view.view, view.KeyHelp()+Views.lookup[view.gui.CurrentView().Name()].KeyHelp()+Formatting.StatusNormal("▏"+strings.Repeat(" ", 1000)))
return nil return nil
}) })
@ -73,7 +75,7 @@ func (view *StatusView) Render() error {
// KeyHelp indicates all the possible global actions a user can take when any pane is selected. // KeyHelp indicates all the possible global actions a user can take when any pane is selected.
func (view *StatusView) KeyHelp() string { func (view *StatusView) KeyHelp() string {
return renderStatusOption("^C","Quit", false) + return renderStatusOption("^C", "Quit", false) +
renderStatusOption("^Space","Switch view", false) + renderStatusOption("^Space", "Switch view", false) +
renderStatusOption("^/","Filter files", Views.Filter.IsVisible()) renderStatusOption("^/", "Filter files", Views.Filter.IsVisible())
} }

View File

@ -5,10 +5,10 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/fatih/color"
"github.com/jroimartin/gocui" "github.com/jroimartin/gocui"
"github.com/wagoodman/dive/filetree" "github.com/wagoodman/dive/filetree"
"github.com/wagoodman/dive/image" "github.com/wagoodman/dive/image"
"github.com/fatih/color"
"os" "os"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
@ -35,14 +35,14 @@ func debugPrint(s string) {
// Formatting defines standard functions for formatting UI sections. // Formatting defines standard functions for formatting UI sections.
var Formatting struct { var Formatting struct {
Header func(...interface{})(string) Header func(...interface{}) string
Selected func(...interface{})(string) Selected func(...interface{}) string
StatusSelected func(...interface{})(string) StatusSelected func(...interface{}) string
StatusNormal func(...interface{})(string) StatusNormal func(...interface{}) string
StatusControlSelected func(...interface{})(string) StatusControlSelected func(...interface{}) string
StatusControlNormal func(...interface{})(string) StatusControlNormal func(...interface{}) string
CompareTop func(...interface{})(string) CompareTop func(...interface{}) string
CompareBottom func(...interface{})(string) CompareBottom func(...interface{}) string
} }
// Views contains all rendered UI panes. // Views contains all rendered UI panes.
@ -84,7 +84,7 @@ func toggleView(g *gocui.Gui, v *gocui.View) error {
func toggleFilterView(g *gocui.Gui, v *gocui.View) error { func toggleFilterView(g *gocui.Gui, v *gocui.View) error {
// delete all user input from the tree view // delete all user input from the tree view
Views.Filter.view.Clear() Views.Filter.view.Clear()
Views.Filter.view.SetCursor(0,0) Views.Filter.view.SetCursor(0, 0)
// toggle hiding // toggle hiding
Views.Filter.hidden = !Views.Filter.hidden Views.Filter.hidden = !Views.Filter.hidden
@ -179,7 +179,6 @@ func isNewView(errs ...error) bool {
return true return true
} }
// layout defines the definition of the window pane size and placement relations to one another. This // layout defines the definition of the window pane size and placement relations to one another. This
// is invoked at application start and whenever the screen dimensions change. // is invoked at application start and whenever the screen dimensions change.
func layout(g *gocui.Gui) error { func layout(g *gocui.Gui) error {
@ -255,12 +254,11 @@ func layout(g *gocui.Gui) error {
// Filter Bar // Filter Bar
view, viewErr = g.SetView(Views.Filter.Name, len(Views.Filter.headerStr)-1, maxY-filterBarHeight-filterBarIndex, maxX, maxY-(filterBarIndex-1)) view, viewErr = g.SetView(Views.Filter.Name, len(Views.Filter.headerStr)-1, maxY-filterBarHeight-filterBarIndex, maxX, maxY-(filterBarIndex-1))
header, headerErr = g.SetView(Views.Filter.Name+"header", -1, maxY-filterBarHeight - filterBarIndex, len(Views.Filter.headerStr), maxY-(filterBarIndex-1)) header, headerErr = g.SetView(Views.Filter.Name+"header", -1, maxY-filterBarHeight-filterBarIndex, len(Views.Filter.headerStr), maxY-(filterBarIndex-1))
if isNewView(viewErr, headerErr) { if isNewView(viewErr, headerErr) {
Views.Filter.Setup(view, header) Views.Filter.Setup(view, header)
} }
return nil return nil
} }
@ -283,9 +281,9 @@ func Render() {
// renderStatusOption formats key help bindings-to-title pairs. // renderStatusOption formats key help bindings-to-title pairs.
func renderStatusOption(control, title string, selected bool) string { func renderStatusOption(control, title string, selected bool) string {
if selected { if selected {
return Formatting.StatusSelected("▏") + Formatting.StatusControlSelected(control) + Formatting.StatusSelected(" " + title + " ") return Formatting.StatusSelected("▏") + Formatting.StatusControlSelected(control) + Formatting.StatusSelected(" "+title+" ")
} else { } else {
return Formatting.StatusNormal("▏") + Formatting.StatusControlNormal(control) + Formatting.StatusNormal(" " + title + " ") return Formatting.StatusNormal("▏") + Formatting.StatusControlNormal(control) + Formatting.StatusNormal(" "+title+" ")
} }
} }
@ -312,7 +310,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,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)
@ -324,7 +322,6 @@ func Run(layers []*image.Layer, refTrees []*filetree.FileTree) {
Views.Details = NewDetailsView("details", g) Views.Details = NewDetailsView("details", g)
Views.lookup[Views.Details.Name] = Views.Details Views.lookup[Views.Details.Name] = Views.Details
g.Cursor = false g.Cursor = false
//g.Mouse = true //g.Mouse = true
g.SetManagerFunc(layout) g.SetManagerFunc(layout)