From 4a15a1d3c8ac54168de49b39dd4d7e853fafac9e Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Fri, 1 Jul 2016 18:12:37 -0400 Subject: [PATCH] Add the ability to close splits and change splits using the mouse This commits adds split navigation with the mouse and the ability to close splits. You can now also open a file directly with the hsplit and vsplit commands. --- cmd/micro/bindings.go | 30 +++++++++++++++++++++++++---- cmd/micro/command.go | 44 +++++++++++++++++++++++++++++++++++++++++-- cmd/micro/micro.go | 8 +++++++- cmd/micro/view.go | 43 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 112 insertions(+), 13 deletions(-) diff --git a/cmd/micro/bindings.go b/cmd/micro/bindings.go index 613b80fb..c3af4cb9 100644 --- a/cmd/micro/bindings.go +++ b/cmd/micro/bindings.go @@ -915,11 +915,13 @@ func (v *View) OpenFile() bool { filename = strings.Replace(filename, "~", home, 1) file, err := ioutil.ReadFile(filename) + var buf *Buffer if err != nil { - messenger.Error(err.Error()) - return false + // File does not exist -- create an empty buffer with that name + buf = NewBuffer([]byte{}, filename) + } else { + buf = NewBuffer(file, filename) } - buf := NewBuffer(file, filename) v.OpenBuffer(buf) return true } @@ -1090,7 +1092,27 @@ func (v *View) Quit() bool { // Make sure not to quit if there are unsaved changes if v.CanClose("Quit anyway? (yes, no, save) ") { v.CloseBuffer() - if len(tabs) > 1 { + if len(tabs[curTab].views) > 1 { + var view *View + if v.splitChild != nil { + view = v.splitChild + view.splitParent = v.splitParent + } else if v.splitParent != nil { + view = v.splitParent + v.splitParent.splitChild = nil + } + view.x, view.y = view.splitOrigPos[0], view.splitOrigPos[1] + view.widthPercent, view.heightPercent = view.splitOrigDimensions[0], view.splitOrigDimensions[1] + view.Resize(screen.Size()) + if settings["syntax"].(bool) { + view.matches = Match(view) + } + tabs[curTab].views = tabs[curTab].views[:v.Num+copy(tabs[curTab].views[v.Num:], tabs[curTab].views[v.Num+1:])] + for i, v := range tabs[curTab].views { + v.Num = i + } + tabs[curTab].curView = view.Num + } else if len(tabs) > 1 { if len(tabs[v.TabNum].views) == 1 { tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])] for i, t := range tabs { diff --git a/cmd/micro/command.go b/cmd/micro/command.go index 05dd7645..54c2aa3d 100644 --- a/cmd/micro/command.go +++ b/cmd/micro/command.go @@ -2,11 +2,14 @@ package main import ( "bytes" + "io/ioutil" "os" "os/exec" "os/signal" "regexp" "strings" + + "github.com/mitchellh/go-homedir" ) var commands map[string]func([]string) @@ -63,11 +66,48 @@ func DefaultCommands() map[string]string { } } +// VSplit opens a vertical split with file given in the first argument +// If no file is given, it opens an empty buffer in a new split func VSplit(args []string) { - CurView().VSplit() + if len(args) == 0 { + CurView().VSplit(NewBuffer([]byte{}, "")) + } else { + filename := args[0] + home, _ := homedir.Dir() + filename = strings.Replace(filename, "~", home, 1) + file, err := ioutil.ReadFile(filename) + + var buf *Buffer + if err != nil { + // File does not exist -- create an empty buffer with that name + buf = NewBuffer([]byte{}, filename) + } else { + buf = NewBuffer(file, filename) + } + CurView().VSplit(buf) + } } + +// HSplit opens a horizontal split with file given in the first argument +// If no file is given, it opens an empty buffer in a new split func HSplit(args []string) { - CurView().HSplit() + if len(args) == 0 { + CurView().HSplit(NewBuffer([]byte{}, "")) + } else { + filename := args[0] + home, _ := homedir.Dir() + filename = strings.Replace(filename, "~", home, 1) + file, err := ioutil.ReadFile(filename) + + var buf *Buffer + if err != nil { + // File does not exist -- create an empty buffer with that name + buf = NewBuffer([]byte{}, filename) + } else { + buf = NewBuffer(file, filename) + } + CurView().HSplit(buf) + } } // Set sets an option diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 859277ab..e62c93fa 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -281,11 +281,17 @@ func main() { case *tcell.EventMouse: if e.Buttons() == tcell.Button1 { _, h := screen.Size() - _, y := e.Position() + x, y := e.Position() if y == h-1 && messenger.message != "" { clipboard.WriteAll(messenger.message) continue } + + for _, v := range tabs[curTab].views { + if x >= v.x && x < v.x+v.width && y >= v.y && y < v.y+v.height { + tabs[curTab].curView = v.Num + } + } } } diff --git a/cmd/micro/view.go b/cmd/micro/view.go index ff004fb9..79d1dfa3 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -87,6 +87,11 @@ type View struct { matches SyntaxMatches // The matches from the last frame lastMatches SyntaxMatches + + splitParent *View + splitChild *View + splitOrigDimensions [2]int + splitOrigPos [2]int } // NewView returns a new fullscreen view @@ -222,32 +227,56 @@ func (v *View) ReOpen() { } } -func (v *View) HSplit() bool { +// HSplit opens a horizontal split with the given buffer +func (v *View) HSplit(buf *Buffer) bool { + origDimensions := [2]int{v.widthPercent, v.heightPercent} + origPos := [2]int{v.x, v.y} + v.heightPercent /= 2 v.Resize(screen.Size()) - newView := NewViewWidthHeight(NewBuffer([]byte{}, ""), v.widthPercent, v.heightPercent) + newView := NewViewWidthHeight(buf, v.widthPercent, v.heightPercent) + + v.splitOrigDimensions = origDimensions + v.splitOrigPos = origPos + newView.splitOrigDimensions = origDimensions + newView.splitOrigPos = origPos + newView.TabNum = v.TabNum newView.y = v.y + v.height + 1 newView.x = v.x tab := tabs[v.TabNum] tab.curView++ newView.Num = len(tab.views) + newView.splitParent = v + v.splitChild = newView tab.views = append(tab.views, newView) return false } -func (v *View) VSplit() bool { +// VSplit opens a vertical split with the given buffer +func (v *View) VSplit(buf *Buffer) bool { + origDimensions := [2]int{v.widthPercent, v.heightPercent} + origPos := [2]int{v.x, v.y} + v.widthPercent /= 2 v.Resize(screen.Size()) - newView := NewViewWidthHeight(NewBuffer([]byte{}, ""), v.widthPercent, v.heightPercent) + newView := NewViewWidthHeight(buf, v.widthPercent, v.heightPercent) + + v.splitOrigDimensions = origDimensions + v.splitOrigPos = origPos + newView.splitOrigDimensions = origDimensions + newView.splitOrigPos = origPos + newView.TabNum = v.TabNum newView.y = v.y newView.x = v.x + v.width tab := tabs[v.TabNum] tab.curView++ newView.Num = len(tab.views) + newView.splitParent = v + v.splitChild = newView tab.views = append(tab.views, newView) return false } @@ -638,7 +667,7 @@ func (v *View) DisplayView() { lineStyle = highlightStyle } - if settings["cursorline"].(bool) && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline { + if settings["cursorline"].(bool) && tabs[curTab].curView == v.Num && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline { if style, ok := colorscheme["cursor-line"]; ok { fg, _, _ := style.Decompose() lineStyle = lineStyle.Background(fg) @@ -742,7 +771,9 @@ func (v *View) DisplayCursor() { // Display renders the view, the cursor, and statusline func (v *View) Display() { v.DisplayView() - v.DisplayCursor() + if v.Num == tabs[curTab].curView { + v.DisplayCursor() + } if settings["statusline"].(bool) { v.sline.Display() }