gofmt
This commit is contained in:
parent
8ca96849da
commit
e700ec3745
@ -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
|
||||||
|
12
cmd/build.go
12
cmd/build.go
@ -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...))
|
||||||
|
|
||||||
|
@ -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...")
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,4 +47,3 @@ package filetree
|
|||||||
// t.Fatalf("Expected %f but got %f", expected, actual)
|
// t.Fatalf("Expected %f but got %f", expected, actual)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
@ -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) {
|
||||||
|
@ -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...
|
||||||
|
@ -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 {
|
||||||
|
@ -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 "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
main.go
1
main.go
@ -33,4 +33,3 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
cmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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])
|
||||||
}
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
31
ui/ui.go
31
ui/ui.go
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user