diff --git a/runtime/ui/controller/controller_collection.go b/runtime/ui/controller/collection.go similarity index 79% rename from runtime/ui/controller/controller_collection.go rename to runtime/ui/controller/collection.go index f0b7c7b..a7c6124 100644 --- a/runtime/ui/controller/controller_collection.go +++ b/runtime/ui/controller/collection.go @@ -9,22 +9,22 @@ import ( ) // var ccOnce sync.Once -var controllers *ControllerCollection +var controllers *Collection -type ControllerCollection struct { +type Collection struct { gui *gocui.Gui - Tree *FileTreeController - Layer *LayerController - Status *StatusController - Filter *FilterController - Details *DetailsController + Tree *FileTree + Layer *Layer + Status *Status + Filter *Filter + Details *Details lookup map[string]Controller } -func NewControllerCollection(g *gocui.Gui, analysis *image.AnalysisResult, cache filetree.TreeCache) (*ControllerCollection, error) { +func NewCollection(g *gocui.Gui, analysis *image.AnalysisResult, cache filetree.TreeCache) (*Collection, error) { var err error - controllers = &ControllerCollection{ + controllers = &Collection{ gui: g, } controllers.lookup = make(map[string]Controller) @@ -56,7 +56,7 @@ func NewControllerCollection(g *gocui.Gui, analysis *image.AnalysisResult, cache return controllers, nil } -func (c *ControllerCollection) UpdateAndRender() error { +func (c *Collection) UpdateAndRender() error { err := c.Update() if err != nil { logrus.Debug("failed update: ", err) @@ -73,7 +73,7 @@ func (c *ControllerCollection) UpdateAndRender() error { } // Update refreshes the state objects for future rendering. -func (c *ControllerCollection) Update() error { +func (c *Collection) Update() error { for _, controller := range c.lookup { err := controller.Update() if err != nil { @@ -85,7 +85,7 @@ func (c *ControllerCollection) Update() error { } // Render flushes the state objects to the screen. -func (c *ControllerCollection) Render() error { +func (c *Collection) Render() error { for _, controller := range c.lookup { if controller.IsVisible() { err := controller.Render() @@ -98,7 +98,7 @@ func (c *ControllerCollection) Render() error { } // ToggleView switches between the file view and the layer view and re-renders the screen. -func (c *ControllerCollection) ToggleView() (err error) { +func (c *Collection) ToggleView() (err error) { v := c.gui.CurrentView() if v == nil || v.Name() == c.Layer.Name() { _, err = c.gui.SetCurrentView(c.Tree.Name()) @@ -114,7 +114,7 @@ func (c *ControllerCollection) ToggleView() (err error) { return c.UpdateAndRender() } -func (c *ControllerCollection) ToggleFilterView() error { +func (c *Collection) ToggleFilterView() error { // delete all user input from the tree view err := c.Filter.ToggleVisible() if err != nil { @@ -135,17 +135,17 @@ func (c *ControllerCollection) ToggleFilterView() error { } // CursorDown moves the cursor down in the currently selected gocui pane, scrolling the screen as needed. -func (c *ControllerCollection) CursorDown(g *gocui.Gui, v *gocui.View) error { +func (c *Collection) CursorDown(g *gocui.Gui, v *gocui.View) error { return c.CursorStep(g, v, 1) } // CursorUp moves the cursor up in the currently selected gocui pane, scrolling the screen as needed. -func (c *ControllerCollection) CursorUp(g *gocui.Gui, v *gocui.View) error { +func (c *Collection) CursorUp(g *gocui.Gui, v *gocui.View) error { return c.CursorStep(g, v, -1) } // Moves the cursor the given step distance, setting the origin to the new cursor line -func (c *ControllerCollection) CursorStep(g *gocui.Gui, v *gocui.View, step int) error { +func (c *Collection) CursorStep(g *gocui.Gui, v *gocui.View, step int) error { cx, cy := v.Cursor() // if there isn't a next line diff --git a/runtime/ui/controller/details_controller.go b/runtime/ui/controller/details.go similarity index 67% rename from runtime/ui/controller/details_controller.go rename to runtime/ui/controller/details.go index d3eccbc..60336e1 100644 --- a/runtime/ui/controller/details_controller.go +++ b/runtime/ui/controller/details.go @@ -14,9 +14,9 @@ import ( "github.com/lunixbochs/vtclean" ) -// DetailsController holds the UI objects and data models for populating the lower-left pane. Specifically the pane that +// Details holds the UI objects and data models for populating the lower-left pane. Specifically the pane that // shows the layer details and image statistics. -type DetailsController struct { +type Details struct { name string gui *gocui.Gui view *gocui.View @@ -26,8 +26,8 @@ type DetailsController struct { } // NewDetailsController creates a new view object attached the the global [gocui] screen object. -func NewDetailsController(name string, gui *gocui.Gui, efficiency float64, inefficiencies filetree.EfficiencySlice) (controller *DetailsController) { - controller = new(DetailsController) +func NewDetailsController(name string, gui *gocui.Gui, efficiency float64, inefficiencies filetree.EfficiencySlice) (controller *Details) { + controller = new(Details) // populate main fields controller.name = name @@ -38,63 +38,63 @@ func NewDetailsController(name string, gui *gocui.Gui, efficiency float64, ineff return controller } -func (controller *DetailsController) Name() string { - return controller.name +func (c *Details) Name() string { + return c.name } // Setup initializes the UI concerns within the context of a global [gocui] view object. -func (controller *DetailsController) Setup(v *gocui.View, header *gocui.View) error { +func (c *Details) Setup(v *gocui.View, header *gocui.View) error { // set controller options - controller.view = v - controller.view.Editable = false - controller.view.Wrap = true - controller.view.Highlight = false - controller.view.Frame = false + c.view = v + c.view.Editable = false + c.view.Wrap = true + c.view.Highlight = false + c.view.Frame = false - controller.header = header - controller.header.Editable = false - controller.header.Wrap = false - controller.header.Frame = false + c.header = header + c.header.Editable = false + c.header.Wrap = false + c.header.Frame = false var infos = []key.BindingInfo{ { Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, - OnAction: controller.CursorDown, + OnAction: c.CursorDown, }, { Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, - OnAction: controller.CursorUp, + OnAction: c.CursorUp, }, } - _, err := key.GenerateBindings(controller.gui, controller.name, infos) + _, err := key.GenerateBindings(c.gui, c.name, infos) if err != nil { return err } - return controller.Render() + return c.Render() } // IsVisible indicates if the details view pane is currently initialized. -func (controller *DetailsController) IsVisible() bool { - return controller != nil +func (c *Details) IsVisible() bool { + return c != nil } // CursorDown moves the cursor down in the details pane (currently indicates nothing). -func (controller *DetailsController) CursorDown() error { - return controllers.CursorDown(controller.gui, controller.view) +func (c *Details) CursorDown() error { + return controllers.CursorDown(c.gui, c.view) } // CursorUp moves the cursor up in the details pane (currently indicates nothing). -func (controller *DetailsController) CursorUp() error { - return controllers.CursorUp(controller.gui, controller.view) +func (c *Details) CursorUp() error { + return controllers.CursorUp(c.gui, c.view) } // Update refreshes the state objects for future rendering. -func (controller *DetailsController) Update() error { +func (c *Details) Update() error { return nil } @@ -103,7 +103,7 @@ func (controller *DetailsController) Update() error { // 2. the image efficiency score // 3. the estimated wasted image space // 4. a list of inefficient file allocations -func (controller *DetailsController) Render() error { +func (c *Details) Render() error { currentLayer := controllers.Layer.currentLayer() var wastedSpace int64 @@ -112,12 +112,12 @@ func (controller *DetailsController) Render() error { inefficiencyReport := fmt.Sprintf(format.Header(template), "Count", "Total Space", "Path") height := 100 - if controller.view != nil { - _, height = controller.view.Size() + if c.view != nil { + _, height = c.view.Size() } - for idx := 0; idx < len(controller.inefficiencies); idx++ { - data := controller.inefficiencies[len(controller.inefficiencies)-1-idx] + for idx := 0; idx < len(c.inefficiencies); idx++ { + data := c.inefficiencies[len(c.inefficiencies)-1-idx] wastedSpace += data.CumulativeSize // todo: make this report scrollable @@ -127,24 +127,24 @@ func (controller *DetailsController) Render() error { } imageSizeStr := fmt.Sprintf("%s %s", format.Header("Total Image size:"), humanize.Bytes(controllers.Layer.ImageSize)) - effStr := fmt.Sprintf("%s %d %%", format.Header("Image efficiency score:"), int(100.0*controller.efficiency)) + effStr := fmt.Sprintf("%s %d %%", format.Header("Image efficiency score:"), int(100.0*c.efficiency)) wastedSpaceStr := fmt.Sprintf("%s %s", format.Header("Potential wasted space:"), humanize.Bytes(uint64(wastedSpace))) - controller.gui.Update(func(g *gocui.Gui) error { + c.gui.Update(func(g *gocui.Gui) error { // update header - controller.header.Clear() - width, _ := controller.view.Size() + c.header.Clear() + width, _ := c.view.Size() layerHeaderStr := fmt.Sprintf("[Layer Details]%s", strings.Repeat("─", width-15)) imageHeaderStr := fmt.Sprintf("[Image Details]%s", strings.Repeat("─", width-15)) - _, err := fmt.Fprintln(controller.header, format.Header(vtclean.Clean(layerHeaderStr, false))) + _, err := fmt.Fprintln(c.header, format.Header(vtclean.Clean(layerHeaderStr, false))) if err != nil { return err } // update contents - controller.view.Clear() + c.view.Clear() var lines = make([]string, 0) if currentLayer.Names != nil && len(currentLayer.Names) > 0 { @@ -162,7 +162,7 @@ func (controller *DetailsController) Render() error { lines = append(lines, effStr+"\n") lines = append(lines, inefficiencyReport) - _, err = fmt.Fprintln(controller.view, strings.Join(lines, "\n")) + _, err = fmt.Fprintln(c.view, strings.Join(lines, "\n")) if err != nil { logrus.Debug("unable to write to buffer: ", err) } @@ -172,6 +172,6 @@ func (controller *DetailsController) Render() error { } // KeyHelp indicates all the possible actions a user can take while the current pane is selected (currently does nothing). -func (controller *DetailsController) KeyHelp() string { +func (c *Details) KeyHelp() string { return "TBD" } diff --git a/runtime/ui/controller/filetree_controller.go b/runtime/ui/controller/filetree.go similarity index 55% rename from runtime/ui/controller/filetree_controller.go rename to runtime/ui/controller/filetree.go index 5dc27db..df10a6b 100644 --- a/runtime/ui/controller/filetree_controller.go +++ b/runtime/ui/controller/filetree.go @@ -20,21 +20,21 @@ const ( type CompareType int -// FileTreeController holds the UI objects and data models for populating the right pane. Specifically the pane that +// FileTree holds the UI objects and data models for populating the right pane. Specifically the pane that // shows selected layer or aggregate file ASCII tree. -type FileTreeController struct { +type FileTree struct { name string gui *gocui.Gui view *gocui.View header *gocui.View - vm *viewmodel.FileTreeViewModel + vm *viewmodel.FileTree helpKeys []*key.Binding } // NewFileTreeController creates a new view object attached the the global [gocui] screen object. -func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (controller *FileTreeController, err error) { - controller = new(FileTreeController) +func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (controller *FileTree, err error) { + controller = new(FileTree) // populate main fields controller.name = name @@ -47,143 +47,143 @@ func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree, return controller, err } -func (controller *FileTreeController) Name() string { - return controller.name +func (c *FileTree) Name() string { + return c.name } -func (controller *FileTreeController) AreAttributesVisible() bool { - return controller.vm.ShowAttributes +func (c *FileTree) AreAttributesVisible() bool { + return c.vm.ShowAttributes } // Setup initializes the UI concerns within the context of a global [gocui] view object. -func (controller *FileTreeController) Setup(v *gocui.View, header *gocui.View) error { +func (c *FileTree) Setup(v *gocui.View, header *gocui.View) error { // set controller options - controller.view = v - controller.view.Editable = false - controller.view.Wrap = false - controller.view.Frame = false + c.view = v + c.view.Editable = false + c.view.Wrap = false + c.view.Frame = false - controller.header = header - controller.header.Editable = false - controller.header.Wrap = false - controller.header.Frame = false + c.header = header + c.header.Editable = false + c.header.Wrap = false + c.header.Frame = false var infos = []key.BindingInfo{ { ConfigKeys: []string{"keybinding.toggle-collapse-dir"}, - OnAction: controller.toggleCollapse, + OnAction: c.toggleCollapse, Display: "Collapse dir", }, { ConfigKeys: []string{"keybinding.toggle-collapse-all-dir"}, - OnAction: controller.toggleCollapseAll, + OnAction: c.toggleCollapseAll, Display: "Collapse all dir", }, { ConfigKeys: []string{"keybinding.toggle-added-files"}, - OnAction: func() error { return controller.toggleShowDiffType(filetree.Added) }, - IsSelected: func() bool { return !controller.vm.HiddenDiffTypes[filetree.Added] }, + OnAction: func() error { return c.toggleShowDiffType(filetree.Added) }, + IsSelected: func() bool { return !c.vm.HiddenDiffTypes[filetree.Added] }, Display: "Added", }, { ConfigKeys: []string{"keybinding.toggle-removed-files"}, - OnAction: func() error { return controller.toggleShowDiffType(filetree.Removed) }, - IsSelected: func() bool { return !controller.vm.HiddenDiffTypes[filetree.Removed] }, + OnAction: func() error { return c.toggleShowDiffType(filetree.Removed) }, + IsSelected: func() bool { return !c.vm.HiddenDiffTypes[filetree.Removed] }, Display: "Removed", }, { ConfigKeys: []string{"keybinding.toggle-modified-files"}, - OnAction: func() error { return controller.toggleShowDiffType(filetree.Modified) }, - IsSelected: func() bool { return !controller.vm.HiddenDiffTypes[filetree.Modified] }, + OnAction: func() error { return c.toggleShowDiffType(filetree.Modified) }, + IsSelected: func() bool { return !c.vm.HiddenDiffTypes[filetree.Modified] }, Display: "Modified", }, { ConfigKeys: []string{"keybinding.toggle-unchanged-files", "keybinding.toggle-unmodified-files"}, - OnAction: func() error { return controller.toggleShowDiffType(filetree.Unmodified) }, - IsSelected: func() bool { return !controller.vm.HiddenDiffTypes[filetree.Unmodified] }, + OnAction: func() error { return c.toggleShowDiffType(filetree.Unmodified) }, + IsSelected: func() bool { return !c.vm.HiddenDiffTypes[filetree.Unmodified] }, Display: "Unmodified", }, { ConfigKeys: []string{"keybinding.toggle-filetree-attributes"}, - OnAction: controller.toggleAttributes, - IsSelected: func() bool { return controller.vm.ShowAttributes }, + OnAction: c.toggleAttributes, + IsSelected: func() bool { return c.vm.ShowAttributes }, Display: "Attributes", }, { ConfigKeys: []string{"keybinding.page-up"}, - OnAction: controller.PageUp, + OnAction: c.PageUp, }, { ConfigKeys: []string{"keybinding.page-down"}, - OnAction: controller.PageDown, + OnAction: c.PageDown, }, { Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, - OnAction: controller.CursorDown, + OnAction: c.CursorDown, }, { Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, - OnAction: controller.CursorUp, + OnAction: c.CursorUp, }, { Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, - OnAction: controller.CursorLeft, + OnAction: c.CursorLeft, }, { Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, - OnAction: controller.CursorRight, + OnAction: c.CursorRight, }, } - helpKeys, err := key.GenerateBindings(controller.gui, controller.name, infos) + helpKeys, err := key.GenerateBindings(c.gui, c.name, infos) if err != nil { return err } - controller.helpKeys = helpKeys + c.helpKeys = helpKeys - _, height := controller.view.Size() - controller.vm.Setup(0, height) - _ = controller.Update() - _ = controller.Render() + _, height := c.view.Size() + c.vm.Setup(0, height) + _ = c.Update() + _ = c.Render() return nil } // IsVisible indicates if the file tree view pane is currently initialized -func (controller *FileTreeController) IsVisible() bool { - return controller != nil +func (c *FileTree) IsVisible() bool { + return c != nil } // ResetCursor moves the cursor back to the top of the buffer and translates to the top of the buffer. -func (controller *FileTreeController) resetCursor() { - _ = controller.view.SetCursor(0, 0) - controller.vm.ResetCursor() +func (c *FileTree) resetCursor() { + _ = c.view.SetCursor(0, 0) + c.vm.ResetCursor() } // SetTreeByLayer populates the view model by stacking the indicated image layer file trees. -func (controller *FileTreeController) setTreeByLayer(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) error { - err := controller.vm.SetTreeByLayer(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop) +func (c *FileTree) setTreeByLayer(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) error { + err := c.vm.SetTreeByLayer(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop) if err != nil { return err } // controller.ResetCursor() - _ = controller.Update() - return controller.Render() + _ = c.Update() + return c.Render() } // CursorDown moves the cursor down and renders the view. // 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 // this range into the view buffer. This is much faster when tree sizes are large. -func (controller *FileTreeController) CursorDown() error { - if controller.vm.CursorDown() { - return controller.Render() +func (c *FileTree) CursorDown() error { + if c.vm.CursorDown() { + return c.Render() } return nil } @@ -192,82 +192,82 @@ func (controller *FileTreeController) CursorDown() error { // 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 // this range into the view buffer. This is much faster when tree sizes are large. -func (controller *FileTreeController) CursorUp() error { - if controller.vm.CursorUp() { - return controller.Render() +func (c *FileTree) CursorUp() error { + if c.vm.CursorUp() { + return c.Render() } return nil } // CursorLeft moves the cursor up until we reach the Parent Node or top of the tree -func (controller *FileTreeController) CursorLeft() error { - err := controller.vm.CursorLeft(filterRegex()) +func (c *FileTree) CursorLeft() error { + err := c.vm.CursorLeft(filterRegex()) if err != nil { return err } - _ = controller.Update() - return controller.Render() + _ = c.Update() + return c.Render() } // CursorRight descends into directory expanding it if needed -func (controller *FileTreeController) CursorRight() error { - err := controller.vm.CursorRight(filterRegex()) +func (c *FileTree) CursorRight() error { + err := c.vm.CursorRight(filterRegex()) if err != nil { return err } - _ = controller.Update() - return controller.Render() + _ = c.Update() + return c.Render() } // PageDown moves to next page putting the cursor on top -func (controller *FileTreeController) PageDown() error { - err := controller.vm.PageDown() +func (c *FileTree) PageDown() error { + err := c.vm.PageDown() if err != nil { return err } - return controller.Render() + return c.Render() } // PageUp moves to previous page putting the cursor on top -func (controller *FileTreeController) PageUp() error { - err := controller.vm.PageUp() +func (c *FileTree) PageUp() error { + err := c.vm.PageUp() if err != nil { return err } - return controller.Render() + return c.Render() } // getAbsPositionNode determines the selected screen cursor's location in the file tree, returning the selected FileNode. -// func (controller *FileTreeController) getAbsPositionNode() (node *filetree.FileNode) { +// func (controller *FileTree) getAbsPositionNode() (node *filetree.FileNode) { // return controller.vm.getAbsPositionNode(filterRegex()) // } // ToggleCollapse will collapse/expand the selected FileNode. -func (controller *FileTreeController) toggleCollapse() error { - err := controller.vm.ToggleCollapse(filterRegex()) +func (c *FileTree) toggleCollapse() error { + err := c.vm.ToggleCollapse(filterRegex()) if err != nil { return err } - _ = controller.Update() - return controller.Render() + _ = c.Update() + return c.Render() } // ToggleCollapseAll will collapse/expand the all directories. -func (controller *FileTreeController) toggleCollapseAll() error { - err := controller.vm.ToggleCollapseAll() +func (c *FileTree) toggleCollapseAll() error { + err := c.vm.ToggleCollapseAll() if err != nil { return err } - if controller.vm.CollapseAll { - controller.resetCursor() + if c.vm.CollapseAll { + c.resetCursor() } - _ = controller.Update() - return controller.Render() + _ = c.Update() + return c.Render() } // ToggleAttributes will show/hide file attributes -func (controller *FileTreeController) toggleAttributes() error { - err := controller.vm.ToggleAttributes() +func (c *FileTree) toggleAttributes() error { + err := c.vm.ToggleAttributes() if err != nil { return err } @@ -276,8 +276,8 @@ func (controller *FileTreeController) toggleAttributes() error { } // ToggleShowDiffType will show/hide the selected DiffType in the filetree pane. -func (controller *FileTreeController) toggleShowDiffType(diffType filetree.DiffType) error { - controller.vm.ToggleShowDiffType(diffType) +func (c *FileTree) toggleShowDiffType(diffType filetree.DiffType) error { + c.vm.ToggleShowDiffType(diffType) // we need to render the changes to the status pane as well (not just this contoller/view) return controllers.UpdateAndRender() } @@ -301,58 +301,58 @@ func filterRegex() *regexp.Regexp { } // OnLayoutChange is called by the UI framework to inform the view-model of the new screen dimensions -func (controller *FileTreeController) OnLayoutChange(resized bool) error { - _ = controller.Update() +func (c *FileTree) OnLayoutChange(resized bool) error { + _ = c.Update() if resized { - return controller.Render() + return c.Render() } return nil } // Update refreshes the state objects for future rendering. -func (controller *FileTreeController) Update() error { +func (c *FileTree) Update() error { var width, height int - if controller.view != nil { - width, height = controller.view.Size() + if c.view != nil { + width, height = c.view.Size() } else { // before the TUI is setup there may not be a controller to reference. Use the entire screen as reference. - width, height = controller.gui.Size() + width, height = c.gui.Size() } // height should account for the header - return controller.vm.Update(filterRegex(), width, height-1) + return c.vm.Update(filterRegex(), width, height-1) } // Render flushes the state objects (file tree) to the pane. -func (controller *FileTreeController) Render() error { +func (c *FileTree) Render() error { title := "Current Layer Contents" if controllers.Layer.CompareMode == CompareAll { title = "Aggregated Layer Contents" } // indicate when selected - if controller.gui.CurrentView() == controller.view { + if c.gui.CurrentView() == c.view { title = "● " + title } - controller.gui.Update(func(g *gocui.Gui) error { + c.gui.Update(func(g *gocui.Gui) error { // update the header - controller.header.Clear() + c.header.Clear() width, _ := g.Size() headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2)) - if controller.vm.ShowAttributes { + if c.vm.ShowAttributes { headerStr += fmt.Sprintf(filetree.AttributeFormat+" %s", "P", "ermission", "UID:GID", "Size", "Filetree") } - _, _ = fmt.Fprintln(controller.header, format.Header(vtclean.Clean(headerStr, false))) + _, _ = fmt.Fprintln(c.header, format.Header(vtclean.Clean(headerStr, false))) // update the contents - controller.view.Clear() - err := controller.vm.Render() + c.view.Clear() + err := c.vm.Render() if err != nil { return err } - _, err = fmt.Fprint(controller.view, controller.vm.Buffer.String()) + _, err = fmt.Fprint(c.view, c.vm.Buffer.String()) return err }) @@ -360,9 +360,9 @@ func (controller *FileTreeController) Render() error { } // KeyHelp indicates all the possible actions a user can take while the current pane is selected. -func (controller *FileTreeController) KeyHelp() string { +func (c *FileTree) KeyHelp() string { var help string - for _, binding := range controller.helpKeys { + for _, binding := range c.helpKeys { help += binding.RenderKeyHelp() } return help diff --git a/runtime/ui/controller/filter_controller.go b/runtime/ui/controller/filter.go similarity index 58% rename from runtime/ui/controller/filter_controller.go rename to runtime/ui/controller/filter.go index 2ff718d..51c0e3b 100644 --- a/runtime/ui/controller/filter_controller.go +++ b/runtime/ui/controller/filter.go @@ -7,9 +7,9 @@ import ( "github.com/wagoodman/dive/runtime/ui/format" ) -// FilterController holds the UI objects and data models for populating the bottom row. Specifically the pane that +// Filter holds the UI objects and data models for populating the bottom row. Specifically the pane that // allows the user to filter the file tree by path. -type FilterController struct { +type Filter struct { name string gui *gocui.Gui view *gocui.View @@ -20,8 +20,8 @@ type FilterController struct { } // NewFilterController creates a new view object attached the the global [gocui] screen object. -func NewFilterController(name string, gui *gocui.Gui) (controller *FilterController) { - controller = new(FilterController) +func NewFilterController(name string, gui *gocui.Gui) (controller *Filter) { + controller = new(Filter) // populate main fields controller.name = name @@ -32,40 +32,40 @@ func NewFilterController(name string, gui *gocui.Gui) (controller *FilterControl return controller } -func (controller *FilterController) Name() string { - return controller.name +func (c *Filter) Name() string { + return c.name } // Setup initializes the UI concerns within the context of a global [gocui] view object. -func (controller *FilterController) Setup(v *gocui.View, header *gocui.View) error { +func (c *Filter) Setup(v *gocui.View, header *gocui.View) error { // set controller options - controller.view = v - controller.maxLength = 200 - controller.view.Frame = false - controller.view.BgColor = gocui.AttrReverse - controller.view.Editable = true - controller.view.Editor = controller + c.view = v + c.maxLength = 200 + c.view.Frame = false + c.view.BgColor = gocui.AttrReverse + c.view.Editable = true + c.view.Editor = c - controller.header = header - controller.header.BgColor = gocui.AttrReverse - controller.header.Editable = false - controller.header.Wrap = false - controller.header.Frame = false + c.header = header + c.header.BgColor = gocui.AttrReverse + c.header.Editable = false + c.header.Wrap = false + c.header.Frame = false - return controller.Render() + return c.Render() } // ToggleFilterView shows/hides the file tree filter pane. -func (controller *FilterController) ToggleVisible() error { +func (c *Filter) ToggleVisible() error { // delete all user input from the tree view - controller.view.Clear() + c.view.Clear() // toggle hiding - controller.hidden = !controller.hidden + c.hidden = !c.hidden - if !controller.hidden { - _, err := controller.gui.SetCurrentView(controller.name) + if !c.hidden { + _, err := c.gui.SetCurrentView(c.name) if err != nil { logrus.Error("unable to toggle filter view: ", err) return err @@ -76,41 +76,41 @@ func (controller *FilterController) ToggleVisible() error { // reset the cursor for the next time it is visible // Note: there is a subtle gocui behavior here where this cannot be called when the view // is newly visible. Is this a problem with dive or gocui? - return controller.view.SetCursor(0, 0) + return c.view.SetCursor(0, 0) } // todo: remove the need for this -func (controller *FilterController) HeaderStr() string { - return controller.headerStr +func (c *Filter) HeaderStr() string { + return c.headerStr } // IsVisible indicates if the filter view pane is currently initialized -func (controller *FilterController) IsVisible() bool { - if controller == nil { +func (c *Filter) IsVisible() bool { + if c == nil { return false } - return !controller.hidden + return !c.hidden } // CursorDown moves the cursor down in the filter pane (currently indicates nothing). -func (controller *FilterController) CursorDown() error { +func (c *Filter) CursorDown() error { return nil } // CursorUp moves the cursor up in the filter pane (currently indicates nothing). -func (controller *FilterController) CursorUp() error { +func (c *Filter) CursorUp() error { return nil } // Edit intercepts the key press events in the filer view to update the file view in real time. -func (controller *FilterController) Edit(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { - if !controller.IsVisible() { +func (c *Filter) Edit(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { + if !c.IsVisible() { return } cx, _ := v.Cursor() ox, _ := v.Origin() - limit := ox+cx+1 > controller.maxLength + limit := ox+cx+1 > c.maxLength switch { case ch != 0 && mod == 0 && !limit: v.EditWrite(ch) @@ -126,14 +126,14 @@ func (controller *FilterController) Edit(v *gocui.View, key gocui.Key, ch rune, } // Update refreshes the state objects for future rendering (currently does nothing). -func (controller *FilterController) Update() error { +func (c *Filter) Update() error { return nil } // Render flushes the state objects to the screen. Currently this is the users path filter input. -func (controller *FilterController) Render() error { - controller.gui.Update(func(g *gocui.Gui) error { - _, err := fmt.Fprintln(controller.header, format.Header(controller.headerStr)) +func (c *Filter) Render() error { + c.gui.Update(func(g *gocui.Gui) error { + _, err := fmt.Fprintln(c.header, format.Header(c.headerStr)) if err != nil { logrus.Error("unable to write to buffer: ", err) } @@ -143,6 +143,6 @@ func (controller *FilterController) Render() error { } // KeyHelp indicates all the possible actions a user can take while the current pane is selected. -func (controller *FilterController) KeyHelp() string { +func (c *Filter) KeyHelp() string { return format.StatusControlNormal("▏Type to filter the file tree ") } diff --git a/runtime/ui/controller/layer_controller.go b/runtime/ui/controller/layer.go similarity index 50% rename from runtime/ui/controller/layer_controller.go rename to runtime/ui/controller/layer.go index a0bf0c0..948a0b1 100644 --- a/runtime/ui/controller/layer_controller.go +++ b/runtime/ui/controller/layer.go @@ -13,9 +13,9 @@ import ( "github.com/spf13/viper" ) -// LayerController holds the UI objects and data models for populating the lower-left pane. Specifically the pane that +// Layer holds the UI objects and data models for populating the lower-left pane. Specifically the pane that // shows the image layers and layer selector. -type LayerController struct { +type Layer struct { name string gui *gocui.Gui view *gocui.View @@ -30,8 +30,8 @@ type LayerController struct { } // NewLayerController creates a new view object attached the the global [gocui] screen object. -func NewLayerController(name string, gui *gocui.Gui, layers []*image.Layer) (controller *LayerController, err error) { - controller = new(LayerController) +func NewLayerController(name string, gui *gocui.Gui, layers []*image.Layer) (controller *Layer, err error) { + controller = new(Layer) // populate main fields controller.name = name @@ -50,196 +50,196 @@ func NewLayerController(name string, gui *gocui.Gui, layers []*image.Layer) (con return controller, err } -func (controller *LayerController) Name() string { - return controller.name +func (c *Layer) Name() string { + return c.name } // Setup initializes the UI concerns within the context of a global [gocui] view object. -func (controller *LayerController) Setup(v *gocui.View, header *gocui.View) error { +func (c *Layer) Setup(v *gocui.View, header *gocui.View) error { // set controller options - controller.view = v - controller.view.Editable = false - controller.view.Wrap = false - controller.view.Frame = false + c.view = v + c.view.Editable = false + c.view.Wrap = false + c.view.Frame = false - controller.header = header - controller.header.Editable = false - controller.header.Wrap = false - controller.header.Frame = false + c.header = header + c.header.Editable = false + c.header.Wrap = false + c.header.Frame = false var infos = []key.BindingInfo{ { ConfigKeys: []string{"keybinding.compare-layer"}, - OnAction: func() error { return controller.setCompareMode(CompareLayer) }, - IsSelected: func() bool { return controller.CompareMode == CompareLayer }, + OnAction: func() error { return c.setCompareMode(CompareLayer) }, + IsSelected: func() bool { return c.CompareMode == CompareLayer }, Display: "Show layer changes", }, { ConfigKeys: []string{"keybinding.compare-all"}, - OnAction: func() error { return controller.setCompareMode(CompareAll) }, - IsSelected: func() bool { return controller.CompareMode == CompareAll }, + OnAction: func() error { return c.setCompareMode(CompareAll) }, + IsSelected: func() bool { return c.CompareMode == CompareAll }, Display: "Show aggregated changes", }, { Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, - OnAction: controller.CursorDown, + OnAction: c.CursorDown, }, { Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, - OnAction: controller.CursorUp, + OnAction: c.CursorUp, }, { Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, - OnAction: controller.CursorUp, + OnAction: c.CursorUp, }, { Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, - OnAction: controller.CursorDown, + OnAction: c.CursorDown, }, { ConfigKeys: []string{"keybinding.page-up"}, - OnAction: controller.PageUp, + OnAction: c.PageUp, }, { ConfigKeys: []string{"keybinding.page-down"}, - OnAction: controller.PageDown, + OnAction: c.PageDown, }, } - helpKeys, err := key.GenerateBindings(controller.gui, controller.name, infos) + helpKeys, err := key.GenerateBindings(c.gui, c.name, infos) if err != nil { return err } - controller.helpKeys = helpKeys + c.helpKeys = helpKeys - return controller.Render() + return c.Render() } // height obtains the height of the current pane (taking into account the lost space due to the header). -func (controller *LayerController) height() uint { - _, height := controller.view.Size() +func (c *Layer) height() uint { + _, height := c.view.Size() return uint(height - 1) } // IsVisible indicates if the layer view pane is currently initialized. -func (controller *LayerController) IsVisible() bool { - return controller != nil +func (c *Layer) IsVisible() bool { + return c != nil } // PageDown moves to next page putting the cursor on top -func (controller *LayerController) PageDown() error { - step := int(controller.height()) + 1 - targetLayerIndex := controller.LayerIndex + step +func (c *Layer) PageDown() error { + step := int(c.height()) + 1 + targetLayerIndex := c.LayerIndex + step - if targetLayerIndex > len(controller.Layers) { - step -= targetLayerIndex - (len(controller.Layers) - 1) + if targetLayerIndex > len(c.Layers) { + step -= targetLayerIndex - (len(c.Layers) - 1) } if step > 0 { - err := controllers.CursorStep(controller.gui, controller.view, step) + err := controllers.CursorStep(c.gui, c.view, step) if err == nil { - return controller.SetCursor(controller.LayerIndex + step) + return c.SetCursor(c.LayerIndex + step) } } return nil } // PageUp moves to previous page putting the cursor on top -func (controller *LayerController) PageUp() error { - step := int(controller.height()) + 1 - targetLayerIndex := controller.LayerIndex - step +func (c *Layer) PageUp() error { + step := int(c.height()) + 1 + targetLayerIndex := c.LayerIndex - step if targetLayerIndex < 0 { step += targetLayerIndex } if step > 0 { - err := controllers.CursorStep(controller.gui, controller.view, -step) + err := controllers.CursorStep(c.gui, c.view, -step) if err == nil { - return controller.SetCursor(controller.LayerIndex - step) + return c.SetCursor(c.LayerIndex - step) } } return nil } // CursorDown moves the cursor down in the layer pane (selecting a higher layer). -func (controller *LayerController) CursorDown() error { - if controller.LayerIndex < len(controller.Layers) { - err := controllers.CursorDown(controller.gui, controller.view) +func (c *Layer) CursorDown() error { + if c.LayerIndex < len(c.Layers) { + err := controllers.CursorDown(c.gui, c.view) if err == nil { - return controller.SetCursor(controller.LayerIndex + 1) + return c.SetCursor(c.LayerIndex + 1) } } return nil } // CursorUp moves the cursor up in the layer pane (selecting a lower layer). -func (controller *LayerController) CursorUp() error { - if controller.LayerIndex > 0 { - err := controllers.CursorUp(controller.gui, controller.view) +func (c *Layer) CursorUp() error { + if c.LayerIndex > 0 { + err := controllers.CursorUp(c.gui, c.view) if err == nil { - return controller.SetCursor(controller.LayerIndex - 1) + return c.SetCursor(c.LayerIndex - 1) } } return nil } // SetCursor resets the cursor and orients the file tree view based on the given layer index. -func (controller *LayerController) SetCursor(layer int) error { - controller.LayerIndex = layer - err := controllers.Tree.setTreeByLayer(controller.getCompareIndexes()) +func (c *Layer) SetCursor(layer int) error { + c.LayerIndex = layer + err := controllers.Tree.setTreeByLayer(c.getCompareIndexes()) if err != nil { return err } _ = controllers.Details.Render() - return controller.Render() + return c.Render() } // currentLayer returns the Layer object currently selected. -func (controller *LayerController) currentLayer() *image.Layer { - return controller.Layers[controller.LayerIndex] +func (c *Layer) currentLayer() *image.Layer { + return c.Layers[c.LayerIndex] } // setCompareMode switches the layer comparison between a single-layer comparison to an aggregated comparison. -func (controller *LayerController) setCompareMode(compareMode CompareType) error { - controller.CompareMode = compareMode +func (c *Layer) setCompareMode(compareMode CompareType) error { + c.CompareMode = compareMode err := controllers.UpdateAndRender() if err != nil { logrus.Errorf("unable to set compare mode: %+v", err) return err } - return controllers.Tree.setTreeByLayer(controller.getCompareIndexes()) + return controllers.Tree.setTreeByLayer(c.getCompareIndexes()) } // getCompareIndexes determines the layer boundaries to use for comparison (based on the current compare mode) -func (controller *LayerController) getCompareIndexes() (bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) { - bottomTreeStart = controller.CompareStartIndex - topTreeStop = controller.LayerIndex +func (c *Layer) getCompareIndexes() (bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) { + bottomTreeStart = c.CompareStartIndex + topTreeStop = c.LayerIndex - if controller.LayerIndex == controller.CompareStartIndex { - bottomTreeStop = controller.LayerIndex - topTreeStart = controller.LayerIndex - } else if controller.CompareMode == CompareLayer { - bottomTreeStop = controller.LayerIndex - 1 - topTreeStart = controller.LayerIndex + if c.LayerIndex == c.CompareStartIndex { + bottomTreeStop = c.LayerIndex + topTreeStart = c.LayerIndex + } else if c.CompareMode == CompareLayer { + bottomTreeStop = c.LayerIndex - 1 + topTreeStart = c.LayerIndex } else { - bottomTreeStop = controller.CompareStartIndex - topTreeStart = controller.CompareStartIndex + 1 + bottomTreeStop = c.CompareStartIndex + topTreeStart = c.CompareStartIndex + 1 } return bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop } // renderCompareBar returns the formatted string for the given layer. -func (controller *LayerController) renderCompareBar(layerIdx int) string { - bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop := controller.getCompareIndexes() +func (c *Layer) renderCompareBar(layerIdx int) string { + bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop := c.getCompareIndexes() result := " " if layerIdx >= bottomTreeStart && layerIdx <= bottomTreeStop { @@ -253,10 +253,10 @@ func (controller *LayerController) renderCompareBar(layerIdx int) string { } // Update refreshes the state objects for future rendering (currently does nothing). -func (controller *LayerController) Update() error { - controller.ImageSize = 0 - for idx := 0; idx < len(controller.Layers); idx++ { - controller.ImageSize += controller.Layers[idx].Size +func (c *Layer) Update() error { + c.ImageSize = 0 + for idx := 0; idx < len(c.Layers); idx++ { + c.ImageSize += c.Layers[idx].Size } return nil } @@ -264,36 +264,36 @@ func (controller *LayerController) Update() error { // Render flushes the state objects to the screen. The layers pane reports: // 1. the layers of the image + metadata // 2. the current selected image -func (controller *LayerController) Render() error { +func (c *Layer) Render() error { // indicate when selected title := "Layers" - if controller.gui.CurrentView() == controller.view { + if c.gui.CurrentView() == c.view { title = "● " + title } - controller.gui.Update(func(g *gocui.Gui) error { + c.gui.Update(func(g *gocui.Gui) error { // update header - controller.header.Clear() + c.header.Clear() width, _ := g.Size() headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2)) headerStr += fmt.Sprintf("Cmp"+image.LayerFormat, "Size", "Command") - _, err := fmt.Fprintln(controller.header, format.Header(vtclean.Clean(headerStr, false))) + _, err := fmt.Fprintln(c.header, format.Header(vtclean.Clean(headerStr, false))) if err != nil { return err } // update contents - controller.view.Clear() - for idx, layer := range controller.Layers { + c.view.Clear() + for idx, layer := range c.Layers { layerStr := layer.String() - compareBar := controller.renderCompareBar(idx) + compareBar := c.renderCompareBar(idx) - if idx == controller.LayerIndex { - _, err = fmt.Fprintln(controller.view, compareBar+" "+format.Selected(layerStr)) + if idx == c.LayerIndex { + _, err = fmt.Fprintln(c.view, compareBar+" "+format.Selected(layerStr)) } else { - _, err = fmt.Fprintln(controller.view, compareBar+" "+layerStr) + _, err = fmt.Fprintln(c.view, compareBar+" "+layerStr) } if err != nil { @@ -308,9 +308,9 @@ func (controller *LayerController) Render() error { } // KeyHelp indicates all the possible actions a user can take while the current pane is selected. -func (controller *LayerController) KeyHelp() string { +func (c *Layer) KeyHelp() string { var help string - for _, binding := range controller.helpKeys { + for _, binding := range c.helpKeys { help += binding.RenderKeyHelp() } return help diff --git a/runtime/ui/controller/status_controller.go b/runtime/ui/controller/status.go similarity index 56% rename from runtime/ui/controller/status_controller.go rename to runtime/ui/controller/status.go index 7de9a30..9a3e612 100644 --- a/runtime/ui/controller/status_controller.go +++ b/runtime/ui/controller/status.go @@ -10,9 +10,9 @@ import ( "github.com/jroimartin/gocui" ) -// StatusController holds the UI objects and data models for populating the bottom-most pane. Specifically the panel +// Status holds the UI objects and data models for populating the bottom-most pane. Specifically the panel // shows the user a set of possible actions to take in the window and currently selected pane. -type StatusController struct { +type Status struct { name string gui *gocui.Gui view *gocui.View @@ -21,8 +21,8 @@ type StatusController struct { } // NewStatusController creates a new view object attached the the global [gocui] screen object. -func NewStatusController(name string, gui *gocui.Gui) (controller *StatusController) { - controller = new(StatusController) +func NewStatusController(name string, gui *gocui.Gui) (controller *Status) { + controller = new(Status) // populate main fields controller.name = name @@ -32,49 +32,49 @@ func NewStatusController(name string, gui *gocui.Gui) (controller *StatusControl return controller } -func (controller *StatusController) Name() string { - return controller.name +func (c *Status) Name() string { + return c.name } -func (controller *StatusController) AddHelpKeys(keys ...*key.Binding) { - controller.helpKeys = append(controller.helpKeys, keys...) +func (c *Status) AddHelpKeys(keys ...*key.Binding) { + c.helpKeys = append(c.helpKeys, keys...) } // Setup initializes the UI concerns within the context of a global [gocui] view object. -func (controller *StatusController) Setup(v *gocui.View, header *gocui.View) error { +func (c *Status) Setup(v *gocui.View, header *gocui.View) error { // set controller options - controller.view = v - controller.view.Frame = false + c.view = v + c.view.Frame = false - return controller.Render() + return c.Render() } // IsVisible indicates if the status view pane is currently initialized. -func (controller *StatusController) IsVisible() bool { - return controller != nil +func (c *Status) IsVisible() bool { + return c != nil } // CursorDown moves the cursor down in the details pane (currently indicates nothing). -func (controller *StatusController) CursorDown() error { +func (c *Status) CursorDown() error { return nil } // CursorUp moves the cursor up in the details pane (currently indicates nothing). -func (controller *StatusController) CursorUp() error { +func (c *Status) CursorUp() error { return nil } // Update refreshes the state objects for future rendering (currently does nothing). -func (controller *StatusController) Update() error { +func (c *Status) Update() error { return nil } // Render flushes the state objects to the screen. -func (controller *StatusController) Render() error { - controller.gui.Update(func(g *gocui.Gui) error { - controller.view.Clear() - _, err := fmt.Fprintln(controller.view, controller.KeyHelp()+format.StatusNormal("▏"+strings.Repeat(" ", 1000))) +func (c *Status) Render() error { + c.gui.Update(func(g *gocui.Gui) error { + c.view.Clear() + _, err := fmt.Fprintln(c.view, c.KeyHelp()+format.StatusNormal("▏"+strings.Repeat(" ", 1000))) if err != nil { logrus.Debug("unable to write to buffer: ", err) } @@ -85,9 +85,9 @@ func (controller *StatusController) Render() error { } // KeyHelp indicates all the possible global actions a user can take when any pane is selected. -func (controller *StatusController) KeyHelp() string { +func (c *Status) KeyHelp() string { var help string - for _, binding := range controller.helpKeys { + for _, binding := range c.helpKeys { help += binding.RenderKeyHelp() } return help diff --git a/runtime/ui/layout_manager.go b/runtime/ui/layout_manager.go index 561f683..fb59d54 100644 --- a/runtime/ui/layout_manager.go +++ b/runtime/ui/layout_manager.go @@ -9,10 +9,10 @@ import ( type layoutManager struct { fileTreeSplitRatio float64 - controllers *controller.ControllerCollection + controllers *controller.Collection } -func newLayoutManager(c *controller.ControllerCollection) *layoutManager { +func newLayoutManager(c *controller.Collection) *layoutManager { fileTreeSplitRatio := viper.GetFloat64("filetree.pane-width") if fileTreeSplitRatio >= 1 || fileTreeSplitRatio <= 0 { diff --git a/runtime/ui/ui.go b/runtime/ui/ui.go index b4fa834..e0cced8 100644 --- a/runtime/ui/ui.go +++ b/runtime/ui/ui.go @@ -16,7 +16,7 @@ const debug = false // type global type app struct { gui *gocui.Gui - controllers *controller.ControllerCollection + controllers *controller.Collection layout *layoutManager } @@ -28,10 +28,10 @@ var ( func newApp(gui *gocui.Gui, analysis *image.AnalysisResult, cache filetree.TreeCache) (*app, error) { var err error once.Do(func() { - var theControls *controller.ControllerCollection + var theControls *controller.Collection var globalHelpKeys []*key.Binding - theControls, err = controller.NewControllerCollection(gui, analysis, cache) + theControls, err = controller.NewCollection(gui, analysis, cache) if err != nil { return } diff --git a/runtime/ui/viewmodel/filetree_viewmodel.go b/runtime/ui/viewmodel/filetree.go similarity index 89% rename from runtime/ui/viewmodel/filetree_viewmodel.go rename to runtime/ui/viewmodel/filetree.go index cf467e6..75cb469 100644 --- a/runtime/ui/viewmodel/filetree_viewmodel.go +++ b/runtime/ui/viewmodel/filetree.go @@ -15,7 +15,7 @@ import ( // FileTreeViewModel holds the UI objects and data models for populating the right pane. Specifically the pane that // shows selected layer or aggregate file ASCII tree. -type FileTreeViewModel struct { +type FileTree struct { ModelTree *filetree.FileTree ViewTree *filetree.FileTree RefTrees []*filetree.FileTree @@ -35,8 +35,8 @@ type FileTreeViewModel struct { } // NewFileTreeViewModel creates a new view object attached the the global [gocui] screen object. -func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (treeViewModel *FileTreeViewModel, err error) { - treeViewModel = new(FileTreeViewModel) +func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (treeViewModel *FileTree, err error) { + treeViewModel = new(FileTree) // populate main fields treeViewModel.ShowAttributes = viper.GetBool("filetree.show-attributes") @@ -66,13 +66,13 @@ func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree } // Setup initializes the UI concerns within the context of a global [gocui] view object. -func (vm *FileTreeViewModel) Setup(lowerBound, height int) { +func (vm *FileTree) Setup(lowerBound, height int) { vm.bufferIndexLowerBound = lowerBound vm.refHeight = height } // height returns the current height and considers the header -func (vm *FileTreeViewModel) height() int { +func (vm *FileTree) height() int { if vm.ShowAttributes { return vm.refHeight - 1 } @@ -80,24 +80,24 @@ func (vm *FileTreeViewModel) height() int { } // bufferIndexUpperBound returns the current upper bounds for the view -func (vm *FileTreeViewModel) bufferIndexUpperBound() int { +func (vm *FileTree) bufferIndexUpperBound() int { return vm.bufferIndexLowerBound + vm.height() } // IsVisible indicates if the file tree view pane is currently initialized -func (vm *FileTreeViewModel) IsVisible() bool { +func (vm *FileTree) IsVisible() bool { return vm != nil } // ResetCursor moves the cursor back to the top of the buffer and translates to the top of the buffer. -func (vm *FileTreeViewModel) ResetCursor() { +func (vm *FileTree) ResetCursor() { vm.TreeIndex = 0 vm.bufferIndex = 0 vm.bufferIndexLowerBound = 0 } // SetTreeByLayer populates the view model by stacking the indicated image layer file trees. -func (vm *FileTreeViewModel) SetTreeByLayer(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) error { +func (vm *FileTree) SetTreeByLayer(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) error { if topTreeStop > len(vm.RefTrees)-1 { return fmt.Errorf("invalid layer index given: %d of %d", topTreeStop, len(vm.RefTrees)-1) } @@ -126,7 +126,7 @@ func (vm *FileTreeViewModel) SetTreeByLayer(bottomTreeStart, bottomTreeStop, top } // doCursorUp performs the internal view's buffer adjustments on cursor up. Note: this is independent of the gocui buffer. -func (vm *FileTreeViewModel) CursorUp() bool { +func (vm *FileTree) CursorUp() bool { if vm.TreeIndex <= 0 { return false } @@ -141,7 +141,7 @@ func (vm *FileTreeViewModel) CursorUp() bool { } // doCursorDown performs the internal view's buffer adjustments on cursor down. Note: this is independent of the gocui buffer. -func (vm *FileTreeViewModel) CursorDown() bool { +func (vm *FileTree) CursorDown() bool { if vm.TreeIndex >= vm.ModelTree.VisibleSize() { return false } @@ -157,7 +157,7 @@ func (vm *FileTreeViewModel) CursorDown() bool { } // CursorLeft moves the cursor up until we reach the Parent Node or top of the tree -func (vm *FileTreeViewModel) CursorLeft(filterRegex *regexp.Regexp) error { +func (vm *FileTree) CursorLeft(filterRegex *regexp.Regexp) error { var visitor func(*filetree.FileNode) error var evaluator func(*filetree.FileNode) bool var dfsCounter, newIndex int @@ -208,7 +208,7 @@ func (vm *FileTreeViewModel) CursorLeft(filterRegex *regexp.Regexp) error { } // CursorRight descends into directory expanding it if needed -func (vm *FileTreeViewModel) CursorRight(filterRegex *regexp.Regexp) error { +func (vm *FileTree) CursorRight(filterRegex *regexp.Regexp) error { node := vm.getAbsPositionNode(filterRegex) if node == nil { return nil @@ -240,7 +240,7 @@ func (vm *FileTreeViewModel) CursorRight(filterRegex *regexp.Regexp) error { } // PageDown moves to next page putting the cursor on top -func (vm *FileTreeViewModel) PageDown() error { +func (vm *FileTree) PageDown() error { nextBufferIndexLowerBound := vm.bufferIndexLowerBound + vm.height() nextBufferIndexUpperBound := nextBufferIndexLowerBound + vm.height() @@ -266,7 +266,7 @@ func (vm *FileTreeViewModel) PageDown() error { } // PageUp moves to previous page putting the cursor on top -func (vm *FileTreeViewModel) PageUp() error { +func (vm *FileTree) PageUp() error { nextBufferIndexLowerBound := vm.bufferIndexLowerBound - vm.height() nextBufferIndexUpperBound := nextBufferIndexLowerBound + vm.height() @@ -291,7 +291,7 @@ func (vm *FileTreeViewModel) PageUp() error { } // getAbsPositionNode determines the selected screen cursor's location in the file tree, returning the selected FileNode. -func (vm *FileTreeViewModel) getAbsPositionNode(filterRegex *regexp.Regexp) (node *filetree.FileNode) { +func (vm *FileTree) getAbsPositionNode(filterRegex *regexp.Regexp) (node *filetree.FileNode) { var visitor func(*filetree.FileNode) error var evaluator func(*filetree.FileNode) bool var dfsCounter int @@ -322,7 +322,7 @@ func (vm *FileTreeViewModel) getAbsPositionNode(filterRegex *regexp.Regexp) (nod } // ToggleCollapse will collapse/expand the selected FileNode. -func (vm *FileTreeViewModel) ToggleCollapse(filterRegex *regexp.Regexp) error { +func (vm *FileTree) ToggleCollapse(filterRegex *regexp.Regexp) error { node := vm.getAbsPositionNode(filterRegex) if node != nil && node.Data.FileInfo.IsDir { node.Data.ViewInfo.Collapsed = !node.Data.ViewInfo.Collapsed @@ -331,7 +331,7 @@ func (vm *FileTreeViewModel) ToggleCollapse(filterRegex *regexp.Regexp) error { } // ToggleCollapseAll will collapse/expand the all directories. -func (vm *FileTreeViewModel) ToggleCollapseAll() error { +func (vm *FileTree) ToggleCollapseAll() error { vm.CollapseAll = !vm.CollapseAll visitor := func(curNode *filetree.FileNode) error { @@ -352,18 +352,18 @@ func (vm *FileTreeViewModel) ToggleCollapseAll() error { } // ToggleCollapse will collapse/expand the selected FileNode. -func (vm *FileTreeViewModel) ToggleAttributes() error { +func (vm *FileTree) ToggleAttributes() error { vm.ShowAttributes = !vm.ShowAttributes return nil } // ToggleShowDiffType will show/hide the selected DiffType in the filetree pane. -func (vm *FileTreeViewModel) ToggleShowDiffType(diffType filetree.DiffType) { +func (vm *FileTree) ToggleShowDiffType(diffType filetree.DiffType) { vm.HiddenDiffTypes[diffType] = !vm.HiddenDiffTypes[diffType] } // Update refreshes the state objects for future rendering. -func (vm *FileTreeViewModel) Update(filterRegex *regexp.Regexp, width, height int) error { +func (vm *FileTree) Update(filterRegex *regexp.Regexp, width, height int) error { vm.refWidth = width vm.refHeight = height @@ -411,7 +411,7 @@ func (vm *FileTreeViewModel) Update(filterRegex *regexp.Regexp, width, height in } // Render flushes the state objects (file tree) to the pane. -func (vm *FileTreeViewModel) Render() error { +func (vm *FileTree) Render() error { treeString := vm.ViewTree.StringBetween(vm.bufferIndexLowerBound, vm.bufferIndexUpperBound(), vm.ShowAttributes) lines := strings.Split(treeString, "\n") diff --git a/runtime/ui/viewmodel/filetree_viewmodel_test.go b/runtime/ui/viewmodel/filetree_test.go similarity index 98% rename from runtime/ui/viewmodel/filetree_viewmodel_test.go rename to runtime/ui/viewmodel/filetree_test.go index 3d92a67..c90f0d7 100644 --- a/runtime/ui/viewmodel/filetree_viewmodel_test.go +++ b/runtime/ui/viewmodel/filetree_test.go @@ -73,7 +73,7 @@ func assertTestData(t *testing.T, actualBytes []byte) { helperCheckDiff(t, expectedBytes, actualBytes) } -func initializeTestViewModel(t *testing.T) *FileTreeViewModel { +func initializeTestViewModel(t *testing.T) *FileTree { result := docker.TestAnalysisFromArchive(t, "../../../.data/test-docker-image.tar") cache := filetree.NewFileTreeCache(result.RefTrees) @@ -95,7 +95,7 @@ func initializeTestViewModel(t *testing.T) *FileTreeViewModel { return vm } -func runTestCase(t *testing.T, vm *FileTreeViewModel, width, height int, filterRegex *regexp.Regexp) { +func runTestCase(t *testing.T, vm *FileTree, width, height int, filterRegex *regexp.Regexp) { err := vm.Update(filterRegex, width, height) if err != nil { t.Errorf("failed to update viewmodel: %v", err)