From 9b9ae89e5937176b4694051e2d98d91333ec3786 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Wed, 15 Jun 2016 18:38:37 -0400 Subject: [PATCH 01/10] Quick first try at vertical splits --- cmd/micro/command.go | 6 ++++++ cmd/micro/view.go | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/cmd/micro/command.go b/cmd/micro/command.go index 6b1fc8ad..cc37bae3 100644 --- a/cmd/micro/command.go +++ b/cmd/micro/command.go @@ -18,6 +18,7 @@ var commandActions = map[string]func([]string){ "Quit": Quit, "Save": Save, "Replace": Replace, + "VSplit": VSplit, } // InitCommands initializes the default commands @@ -56,9 +57,14 @@ func DefaultCommands() map[string]string { "quit": "Quit", "save": "Save", "replace": "Replace", + "vsplit": "VSplit", } } +func VSplit(args []string) { + CurView().VSplit() +} + // Set sets an option func Set(args []string) { // Set an option and we have to set it for every view diff --git a/cmd/micro/view.go b/cmd/micro/view.go index a3b4b176..e0bc2eed 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -218,6 +218,20 @@ func (v *View) ReOpen() { } } +func (v *View) VSplit() bool { + v.widthPercent /= 2 + v.Resize(screen.Size()) + + newView := NewViewWidthHeight(NewBuffer([]byte{}, ""), v.widthPercent, v.heightPercent) + newView.TabNum = v.TabNum + newView.x = v.x + v.width + tab := tabs[v.TabNum] + tab.curView++ + newView.Num = len(tab.views) + tab.views = append(tab.views, newView) + return false +} + // Relocate moves the view window so that the cursor is in view // This is useful if the user has scrolled far away, and then starts typing func (v *View) Relocate() bool { @@ -340,7 +354,7 @@ func (v *View) HandleEvent(event tcell.Event) { v.freshClip = false case *tcell.EventMouse: x, y := e.Position() - x -= v.lineNumOffset - v.leftCol + x -= v.lineNumOffset - v.leftCol + v.x y += v.Topline - v.y // Don't relocate for mouse events relocate = false From 7e0286b6213a59692c1ac59bb45f6728a1c95473 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Sun, 26 Jun 2016 16:18:33 -0400 Subject: [PATCH 02/10] Fix redrawing for splits --- cmd/micro/micro.go | 4 ++-- cmd/micro/statusline.go | 7 ++++--- cmd/micro/view.go | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 69ac0827..859277ab 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -174,11 +174,11 @@ func InitScreen() { // RedrawAll redraws everything -- all the views and the messenger func RedrawAll() { messenger.Clear() + DisplayTabs() + messenger.Display() for _, v := range tabs[curTab].views { v.Display() } - DisplayTabs() - messenger.Display() screen.Show() } diff --git a/cmd/micro/statusline.go b/cmd/micro/statusline.go index af1ad973..271f8d21 100644 --- a/cmd/micro/statusline.go +++ b/cmd/micro/statusline.go @@ -48,13 +48,14 @@ func (sline *Statusline) Display() { // Maybe there is a unicode filename? fileRunes := []rune(file) + viewX := sline.view.x for x := 0; x < sline.view.width; x++ { if x < len(fileRunes) { - screen.SetContent(x, y, fileRunes[x], nil, statusLineStyle) + screen.SetContent(viewX+x, y, fileRunes[x], nil, statusLineStyle) } else if x >= sline.view.width-len(rightText) && x < len(rightText)+sline.view.width-len(rightText) { - screen.SetContent(x, y, []rune(rightText)[x-sline.view.width+len(rightText)], nil, statusLineStyle) + screen.SetContent(viewX+x, y, []rune(rightText)[x-sline.view.width+len(rightText)], nil, statusLineStyle) } else { - screen.SetContent(x, y, ' ', nil, statusLineStyle) + screen.SetContent(viewX+x, y, ' ', nil, statusLineStyle) } } } diff --git a/cmd/micro/view.go b/cmd/micro/view.go index e0bc2eed..44fa6f8a 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -502,7 +502,7 @@ func (v *View) DisplayView() { // If the buffer is smaller than the view height if lineN+v.Topline >= v.Buf.NumLines { // We have to clear all this space - for i := 0; i < v.width; i++ { + for i := v.x; i < v.width; i++ { screen.SetContent(i, lineN+v.y, ' ', nil, defStyle) } @@ -677,7 +677,7 @@ func (v *View) DisplayView() { charNum = charNum.Move(1, v.Buf) - for i := 0; i < v.width-(x-v.leftCol); i++ { + for i := 0; i < v.width-((x-v.x)-v.leftCol); i++ { lineStyle := defStyle if settings["cursorline"].(bool) && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline { if style, ok := colorscheme["cursor-line"]; ok { From bcbef1c633e24251533c19193e3a4f661507225b Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Sun, 26 Jun 2016 18:44:15 -0400 Subject: [PATCH 03/10] Add horizontal splits --- cmd/micro/command.go | 5 +++++ cmd/micro/view.go | 46 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/cmd/micro/command.go b/cmd/micro/command.go index cc37bae3..05dd7645 100644 --- a/cmd/micro/command.go +++ b/cmd/micro/command.go @@ -19,6 +19,7 @@ var commandActions = map[string]func([]string){ "Save": Save, "Replace": Replace, "VSplit": VSplit, + "HSplit": HSplit, } // InitCommands initializes the default commands @@ -58,12 +59,16 @@ func DefaultCommands() map[string]string { "save": "Save", "replace": "Replace", "vsplit": "VSplit", + "hsplit": "HSplit", } } func VSplit(args []string) { CurView().VSplit() } +func HSplit(args []string) { + CurView().HSplit() +} // Set sets an option func Set(args []string) { diff --git a/cmd/micro/view.go b/cmd/micro/view.go index 44fa6f8a..7060716d 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -123,13 +123,13 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View { func (v *View) Resize(w, h int) { // Always include 1 line for the command line at the bottom h-- - if len(tabs) > 1 { - // Include one line for the tab bar at the top - h-- - v.y = 1 - } else { - v.y = 0 - } + // if len(tabs) > 1 { + // // Include one line for the tab bar at the top + // h-- + // v.y = 1 + // } else { + // v.y = 0 + // } v.width = int(float32(w) * float32(v.widthPercent) / 100) // We subtract 1 for the statusline v.height = int(float32(h) * float32(v.heightPercent) / 100) @@ -218,12 +218,28 @@ func (v *View) ReOpen() { } } +func (v *View) HSplit() bool { + v.heightPercent /= 2 + v.Resize(screen.Size()) + + newView := NewViewWidthHeight(NewBuffer([]byte{}, ""), v.widthPercent, v.heightPercent) + 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) + tab.views = append(tab.views, newView) + return false +} + func (v *View) VSplit() bool { v.widthPercent /= 2 v.Resize(screen.Size()) newView := NewViewWidthHeight(NewBuffer([]byte{}, ""), v.widthPercent, v.heightPercent) newView.TabNum = v.TabNum + newView.y = v.y newView.x = v.x + v.width tab := tabs[v.TabNum] tab.curView++ @@ -497,12 +513,22 @@ func (v *View) DisplayView() { v.lineNumOffset += 2 } + if v.x != 0 { + // One space for the extra split divider + v.lineNumOffset++ + } + for lineN := 0; lineN < v.height; lineN++ { x := v.x + if v.x != 0 { + // Draw the split divider + screen.SetContent(x, lineN+v.y, ' ', nil, defStyle.Reverse(true)) + x++ + } // If the buffer is smaller than the view height if lineN+v.Topline >= v.Buf.NumLines { // We have to clear all this space - for i := v.x; i < v.width; i++ { + for i := x; i < v.x+v.width; i++ { screen.SetContent(i, lineN+v.y, ' ', nil, defStyle) } @@ -642,12 +668,12 @@ func (v *View) DisplayView() { } } else if runewidth.RuneWidth(ch) > 1 { if x-v.leftCol >= v.lineNumOffset { - screen.SetContent(x-v.leftCol, lineN, ch, nil, lineStyle) + screen.SetContent(x-v.leftCol, lineN+v.y, ch, nil, lineStyle) } for i := 0; i < runewidth.RuneWidth(ch)-1; i++ { x++ if x-v.leftCol >= v.lineNumOffset { - screen.SetContent(x-v.leftCol, lineN, ' ', nil, lineStyle) + screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle) } } } else { From d419e65a03e0f47171b64fdc14cfcfb8dc43e65f Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Mon, 27 Jun 2016 18:41:04 -0400 Subject: [PATCH 04/10] Make sure splits can't draw in other splits --- cmd/micro/statusline.go | 4 ++++ cmd/micro/view.go | 39 +++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/cmd/micro/statusline.go b/cmd/micro/statusline.go index 271f8d21..0c72f517 100644 --- a/cmd/micro/statusline.go +++ b/cmd/micro/statusline.go @@ -49,6 +49,10 @@ func (sline *Statusline) Display() { // Maybe there is a unicode filename? fileRunes := []rune(file) viewX := sline.view.x + if viewX != 0 { + screen.SetContent(viewX, y, ' ', nil, statusLineStyle) + viewX++ + } for x := 0; x < sline.view.width; x++ { if x < len(fileRunes) { screen.SetContent(viewX+x, y, fileRunes[x], nil, statusLineStyle) diff --git a/cmd/micro/view.go b/cmd/micro/view.go index 7060716d..7d26e7b5 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -487,8 +487,15 @@ func (v *View) ClearAllGutterMessages() { } } +func (v *View) drawCell(x, y int, ch rune, combc []rune, style tcell.Style) { + if x >= v.x && x < v.x+v.width && y >= v.y && y < v.y+v.height { + screen.SetContent(x, y, ch, combc, style) + } +} + // DisplayView renders the view to the screen func (v *View) DisplayView() { + // The character number of the character in the top left of the screen charNum := Loc{0, v.Topline} @@ -522,14 +529,14 @@ func (v *View) DisplayView() { x := v.x if v.x != 0 { // Draw the split divider - screen.SetContent(x, lineN+v.y, ' ', nil, defStyle.Reverse(true)) + v.drawCell(x, lineN+v.y, ' ', nil, defStyle.Reverse(true)) x++ } // If the buffer is smaller than the view height if lineN+v.Topline >= v.Buf.NumLines { // We have to clear all this space for i := x; i < v.x+v.width; i++ { - screen.SetContent(i, lineN+v.y, ' ', nil, defStyle) + v.drawCell(i, lineN+v.y, ' ', nil, defStyle) } continue @@ -557,9 +564,9 @@ func (v *View) DisplayView() { gutterStyle = style } } - screen.SetContent(x, lineN+v.y, '>', nil, gutterStyle) + v.drawCell(x, lineN+v.y, '>', nil, gutterStyle) x++ - screen.SetContent(x, lineN+v.y, '>', nil, gutterStyle) + v.drawCell(x, lineN+v.y, '>', nil, gutterStyle) x++ if v.Cursor.Y == lineN+v.Topline { messenger.Message(msg.msg) @@ -569,9 +576,9 @@ func (v *View) DisplayView() { } } if !msgOnLine { - screen.SetContent(x, lineN+v.y, ' ', nil, defStyle) + v.drawCell(x, lineN+v.y, ' ', nil, defStyle) x++ - screen.SetContent(x, lineN+v.y, ' ', nil, defStyle) + v.drawCell(x, lineN+v.y, ' ', nil, defStyle) x++ if v.Cursor.Y == lineN+v.Topline && messenger.gutterMessage { messenger.Reset() @@ -590,18 +597,18 @@ func (v *View) DisplayView() { if settings["ruler"] == true { lineNum = strconv.Itoa(lineN + v.Topline + 1) for i := 0; i < maxLineLength-len(lineNum); i++ { - screen.SetContent(x, lineN+v.y, ' ', nil, lineNumStyle) + v.drawCell(x, lineN+v.y, ' ', nil, lineNumStyle) x++ } // Write the actual line number for _, ch := range lineNum { - screen.SetContent(x, lineN+v.y, ch, nil, lineNumStyle) + v.drawCell(x, lineN+v.y, ch, nil, lineNumStyle) x++ } if settings["ruler"] == true { // Write the extra space - screen.SetContent(x, lineN+v.y, ' ', nil, lineNumStyle) + v.drawCell(x, lineN+v.y, ' ', nil, lineNumStyle) x++ } } @@ -657,28 +664,28 @@ func (v *View) DisplayView() { } indentChar := []rune(settings["indentchar"].(string)) if x-v.leftCol >= v.lineNumOffset { - screen.SetContent(x-v.leftCol, lineN+v.y, indentChar[0], nil, lineIndentStyle) + v.drawCell(x-v.leftCol, lineN+v.y, indentChar[0], nil, lineIndentStyle) } tabSize := int(settings["tabsize"].(float64)) for i := 0; i < tabSize-1; i++ { x++ if x-v.leftCol >= v.lineNumOffset { - screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle) + v.drawCell(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle) } } } else if runewidth.RuneWidth(ch) > 1 { if x-v.leftCol >= v.lineNumOffset { - screen.SetContent(x-v.leftCol, lineN+v.y, ch, nil, lineStyle) + v.drawCell(x-v.leftCol, lineN+v.y, ch, nil, lineStyle) } for i := 0; i < runewidth.RuneWidth(ch)-1; i++ { x++ if x-v.leftCol >= v.lineNumOffset { - screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle) + v.drawCell(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle) } } } else { if x-v.leftCol >= v.lineNumOffset { - screen.SetContent(x-v.leftCol, lineN+v.y, ch, nil, lineStyle) + v.drawCell(x-v.leftCol, lineN+v.y, ch, nil, lineStyle) } } charNum = charNum.Move(1, v.Buf) @@ -697,7 +704,7 @@ func (v *View) DisplayView() { if style, ok := colorscheme["selection"]; ok { selectStyle = style } - screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, selectStyle) + v.drawCell(x-v.leftCol, lineN+v.y, ' ', nil, selectStyle) x++ } @@ -712,7 +719,7 @@ func (v *View) DisplayView() { } } if !(x-v.leftCol < v.lineNumOffset) { - screen.SetContent(x+i, lineN+v.y, ' ', nil, lineStyle) + v.drawCell(x+i, lineN+v.y, ' ', nil, lineStyle) } } } From d2b11c2f982fa14d3c931c812cbc202621bb4c35 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Tue, 28 Jun 2016 21:33:10 -0400 Subject: [PATCH 05/10] Fix tabbar offset with splits --- cmd/micro/highlighter.go | 3 +++ cmd/micro/view.go | 18 +++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cmd/micro/highlighter.go b/cmd/micro/highlighter.go index 03c85e2f..32fe6563 100644 --- a/cmd/micro/highlighter.go +++ b/cmd/micro/highlighter.go @@ -152,6 +152,9 @@ func LoadSyntaxFilesFromDir(dir string) { if style, ok := colorscheme["default"]; ok { defStyle = style } + if screen != nil { + screen.SetStyle(defStyle) + } syntaxFiles = make(map[[2]*regexp.Regexp]FileTypeRules) files, _ := ioutil.ReadDir(dir) diff --git a/cmd/micro/view.go b/cmd/micro/view.go index 7d26e7b5..ff004fb9 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -123,13 +123,17 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View { func (v *View) Resize(w, h int) { // Always include 1 line for the command line at the bottom h-- - // if len(tabs) > 1 { - // // Include one line for the tab bar at the top - // h-- - // v.y = 1 - // } else { - // v.y = 0 - // } + if len(tabs) > 1 { + if v.y == 0 { + // Include one line for the tab bar at the top + h-- + v.y = 1 + } + } else { + if v.y == 1 { + v.y = 0 + } + } v.width = int(float32(w) * float32(v.widthPercent) / 100) // We subtract 1 for the statusline v.height = int(float32(h) * float32(v.heightPercent) / 100) From 4a15a1d3c8ac54168de49b39dd4d7e853fafac9e Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Fri, 1 Jul 2016 18:12:37 -0400 Subject: [PATCH 06/10] 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() } From 3089967546c5d5a777ce3027a2422b26c02cc752 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Sat, 2 Jul 2016 10:01:05 -0400 Subject: [PATCH 07/10] Add binding to move to next split (default binding: CtrlW) --- cmd/micro/bindings.go | 25 +++++++++++++++++++++++++ cmd/micro/view.go | 4 ++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/cmd/micro/bindings.go b/cmd/micro/bindings.go index c3af4cb9..8ef5ada4 100644 --- a/cmd/micro/bindings.go +++ b/cmd/micro/bindings.go @@ -76,6 +76,8 @@ var bindingActions = map[string]func(*View) bool{ "AddTab": (*View).AddTab, "PreviousTab": (*View).PreviousTab, "NextTab": (*View).NextTab, + "NextSplit": (*View).NextSplit, + "PreviousSplit": (*View).PreviousSplit, } var bindingKeys = map[string]tcell.Key{ @@ -398,6 +400,7 @@ func DefaultBindings() map[string]string { "CtrlB": "ShellMode", "CtrlQ": "Quit", "CtrlE": "CommandMode", + "CtrlW": "NextSplit", // Emacs-style keybindings "Alt-f": "WordRight", @@ -1170,6 +1173,28 @@ func (v *View) NextTab() bool { return false } +// Changes the view to the next split +func (v *View) NextSplit() bool { + tab := tabs[curTab] + if tab.curView < len(tab.views)-1 { + tab.curView++ + } else { + tab.curView = 0 + } + return false +} + +// Changes the view to the previous split +func (v *View) PreviousSplit() bool { + tab := tabs[curTab] + if tab.curView > 0 { + tab.curView-- + } else { + tab.curView = len(tab.views) - 1 + } + return false +} + // None is no action func None() bool { return false diff --git a/cmd/micro/view.go b/cmd/micro/view.go index 79d1dfa3..2f5a15a4 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -689,7 +689,7 @@ func (v *View) DisplayView() { lineIndentStyle = style } } - 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() lineIndentStyle = lineIndentStyle.Background(fg) @@ -745,7 +745,7 @@ func (v *View) DisplayView() { for i := 0; i < v.width-((x-v.x)-v.leftCol); i++ { lineStyle := defStyle - 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) From f01dc89171cb823d2b0a5e9e51b2b87b19763015 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Tue, 5 Jul 2016 18:46:50 -0400 Subject: [PATCH 08/10] Fix extra line being added with odd heights --- cmd/micro/view.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmd/micro/view.go b/cmd/micro/view.go index 2f5a15a4..f500f2dc 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -142,6 +142,13 @@ func (v *View) Resize(w, h int) { v.width = int(float32(w) * float32(v.widthPercent) / 100) // We subtract 1 for the statusline v.height = int(float32(h) * float32(v.heightPercent) / 100) + if w%2 == 1 && v.x > 1 && v.widthPercent < 100 { + v.width++ + } + + if h%2 == 1 && v.y > 1 && v.heightPercent < 100 { + v.height++ + } if settings["statusline"].(bool) { // Make room for the status line if it is enabled v.height-- @@ -251,6 +258,7 @@ func (v *View) HSplit(buf *Buffer) bool { newView.splitParent = v v.splitChild = newView tab.views = append(tab.views, newView) + newView.Resize(screen.Size()) return false } @@ -278,6 +286,7 @@ func (v *View) VSplit(buf *Buffer) bool { newView.splitParent = v v.splitChild = newView tab.views = append(tab.views, newView) + newView.Resize(screen.Size()) return false } From 271b127f795e59bd029caf69fc81ed6a4fcf227b Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Sat, 9 Jul 2016 11:29:22 -0400 Subject: [PATCH 09/10] Fix small glitch with splits --- cmd/micro/view.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/micro/view.go b/cmd/micro/view.go index f500f2dc..79047f4a 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -142,7 +142,7 @@ func (v *View) Resize(w, h int) { v.width = int(float32(w) * float32(v.widthPercent) / 100) // We subtract 1 for the statusline v.height = int(float32(h) * float32(v.heightPercent) / 100) - if w%2 == 1 && v.x > 1 && v.widthPercent < 100 { + if w%2 == 0 && v.x > 1 && v.widthPercent < 100 { v.width++ } From 8f12c89de188e6b284785d52c84979a5830d0eeb Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Sat, 9 Jul 2016 11:40:46 -0400 Subject: [PATCH 10/10] Add documentation for splits --- runtime/help/help.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runtime/help/help.md b/runtime/help/help.md index 031382b8..446e1713 100644 --- a/runtime/help/help.md +++ b/runtime/help/help.md @@ -78,6 +78,7 @@ you can rebind them to your liking. "CtrlB": "ShellMode", "CtrlQ": "Quit", "CtrlE": "CommandMode", + "CtrlW": "NextSplit", // Emacs-style keybindings "Alt-f": "WordRight", @@ -114,6 +115,7 @@ You can execute an editor command by pressing `Ctrl-e` followed by the command. Here are the possible commands that you can use. * `quit`: Quits micro. + * `save`: Saves the current buffer. * `replace "search" "value" flags`: This will replace `search` with `value`. @@ -133,6 +135,12 @@ Here are the possible commands that you can use. * `bind key action`: creates a keybinding from key to action. See the sections on keybindings above for more info about what keys and actions are available. +* `vsplit filename`: opens a vertical split with `filename`. If no filename is + provided, a vertical split is opened with an empty buffer + +* `hsplit filename`: same as `vsplit` but opens a horizontal split instead of + a vertical split + ### Options Micro stores all of the user configuration in its configuration directory.