]> git.lizzy.rs Git - micro.git/blobdiff - internal/action/actions.go
More style improvements
[micro.git] / internal / action / actions.go
index ce2d86c7c6169971a40c29e0fd812cdf2e2d2ccc..08bd4c7294d9911a8b30c939f60935c154610644 100644 (file)
@@ -1,39 +1,48 @@
 package action
 
 import (
+       "errors"
+       "fmt"
+       "io/fs"
        "regexp"
        "runtime"
        "strings"
        "time"
 
        shellquote "github.com/kballard/go-shellquote"
-       "github.com/zyedidia/clipboard"
        "github.com/zyedidia/micro/v2/internal/buffer"
+       "github.com/zyedidia/micro/v2/internal/clipboard"
        "github.com/zyedidia/micro/v2/internal/config"
+       "github.com/zyedidia/micro/v2/internal/display"
        "github.com/zyedidia/micro/v2/internal/screen"
        "github.com/zyedidia/micro/v2/internal/shell"
        "github.com/zyedidia/micro/v2/internal/util"
-       "github.com/zyedidia/tcell"
+       "github.com/zyedidia/tcell/v2"
 )
 
 // ScrollUp is not an action
 func (h *BufPane) ScrollUp(n int) {
        v := h.GetView()
-       if v.StartLine >= n {
-               v.StartLine -= n
-               h.SetView(v)
-       } else {
-               v.StartLine = 0
-       }
+       v.StartLine = h.Scroll(v.StartLine, -n)
+       h.SetView(v)
 }
 
 // ScrollDown is not an action
 func (h *BufPane) ScrollDown(n int) {
        v := h.GetView()
-       if v.StartLine <= h.Buf.LinesNum()-1-n {
-               v.StartLine += n
-               h.SetView(v)
+       v.StartLine = h.Scroll(v.StartLine, n)
+       h.SetView(v)
+}
+
+// ScrollAdjust can be used to shift the view so that the last line is at the
+// bottom if the user has scrolled past the last line.
+func (h *BufPane) ScrollAdjust() {
+       v := h.GetView()
+       end := h.SLocFromLoc(h.Buf.End())
+       if h.Diff(v.StartLine, end) < h.BufView().Height-1 {
+               v.StartLine = h.Scroll(end, -h.BufView().Height+1)
        }
+       h.SetView(v)
 }
 
 // MousePress is the event that should happen when a normal click happens
@@ -59,7 +68,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
                                h.doubleClick = false
 
                                h.Cursor.SelectLine()
-                               h.Cursor.CopySelection("primary")
+                               h.Cursor.CopySelection(clipboard.PrimaryReg)
                        } else {
                                // Double click
                                h.lastClickTime = time.Now()
@@ -68,7 +77,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
                                h.tripleClick = false
 
                                h.Cursor.SelectWord()
-                               h.Cursor.CopySelection("primary")
+                               h.Cursor.CopySelection(clipboard.PrimaryReg)
                        }
                } else {
                        h.doubleClick = false
@@ -92,6 +101,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
 
        h.Cursor.StoreVisualX()
        h.lastLoc = mouseLoc
+       h.Relocate()
        return true
 }
 
@@ -110,22 +120,55 @@ func (h *BufPane) ScrollDownAction() bool {
 // Center centers the view on the cursor
 func (h *BufPane) Center() bool {
        v := h.GetView()
-       v.StartLine = h.Cursor.Y - v.Height/2
-       if v.StartLine+v.Height > h.Buf.LinesNum() {
-               v.StartLine = h.Buf.LinesNum() - v.Height
-       }
-       if v.StartLine < 0 {
-               v.StartLine = 0
-       }
+       v.StartLine = h.Scroll(h.SLocFromLoc(h.Cursor.Loc), -h.BufView().Height/2)
        h.SetView(v)
-       h.Relocate()
+       h.ScrollAdjust()
        return true
 }
 
+// MoveCursorUp is not an action
+func (h *BufPane) MoveCursorUp(n int) {
+       if !h.Buf.Settings["softwrap"].(bool) {
+               h.Cursor.UpN(n)
+       } else {
+               vloc := h.VLocFromLoc(h.Cursor.Loc)
+               sloc := h.Scroll(vloc.SLoc, -n)
+               if sloc == vloc.SLoc {
+                       // we are at the beginning of buffer
+                       h.Cursor.Loc = h.Buf.Start()
+                       h.Cursor.LastVisualX = 0
+               } else {
+                       vloc.SLoc = sloc
+                       vloc.VisualX = h.Cursor.LastVisualX
+                       h.Cursor.Loc = h.LocFromVLoc(vloc)
+               }
+       }
+}
+
+// MoveCursorDown is not an action
+func (h *BufPane) MoveCursorDown(n int) {
+       if !h.Buf.Settings["softwrap"].(bool) {
+               h.Cursor.DownN(n)
+       } else {
+               vloc := h.VLocFromLoc(h.Cursor.Loc)
+               sloc := h.Scroll(vloc.SLoc, n)
+               if sloc == vloc.SLoc {
+                       // we are at the end of buffer
+                       h.Cursor.Loc = h.Buf.End()
+                       vloc = h.VLocFromLoc(h.Cursor.Loc)
+                       h.Cursor.LastVisualX = vloc.VisualX
+               } else {
+                       vloc.SLoc = sloc
+                       vloc.VisualX = h.Cursor.LastVisualX
+                       h.Cursor.Loc = h.LocFromVLoc(vloc)
+               }
+       }
+}
+
 // CursorUp moves the cursor up
 func (h *BufPane) CursorUp() bool {
        h.Cursor.Deselect(true)
-       h.Cursor.Up()
+       h.MoveCursorUp(1)
        h.Relocate()
        return true
 }
@@ -133,7 +176,7 @@ func (h *BufPane) CursorUp() bool {
 // CursorDown moves the cursor down
 func (h *BufPane) CursorDown() bool {
        h.Cursor.Deselect(true)
-       h.Cursor.Down()
+       h.MoveCursorDown(1)
        h.Relocate()
        return true
 }
@@ -211,7 +254,7 @@ func (h *BufPane) SelectUp() bool {
        if !h.Cursor.HasSelection() {
                h.Cursor.OrigSelection[0] = h.Cursor.Loc
        }
-       h.Cursor.Up()
+       h.MoveCursorUp(1)
        h.Cursor.SelectTo(h.Cursor.Loc)
        h.Relocate()
        return true
@@ -222,7 +265,7 @@ func (h *BufPane) SelectDown() bool {
        if !h.Cursor.HasSelection() {
                h.Cursor.OrigSelection[0] = h.Cursor.Loc
        }
-       h.Cursor.Down()
+       h.MoveCursorDown(1)
        h.Cursor.SelectTo(h.Cursor.Loc)
        h.Relocate()
        return true
@@ -767,7 +810,7 @@ func (h *BufPane) SaveAs() bool {
 func (h *BufPane) saveBufToFile(filename string, action string, callback func()) bool {
        err := h.Buf.SaveAs(filename)
        if err != nil {
-               if strings.HasSuffix(err.Error(), "permission denied") {
+               if errors.Is(err, fs.ErrPermission) {
                        saveWithSudo := func() {
                                err = h.Buf.SaveAsWithSudo(filename)
                                if err != nil {
@@ -784,12 +827,15 @@ func (h *BufPane) saveBufToFile(filename string, action string, callback func())
                        if h.Buf.Settings["autosu"].(bool) {
                                saveWithSudo()
                        } else {
-                               InfoBar.YNPrompt("Permission denied. Do you want to save this file using sudo? (y,n)", func(yes, canceled bool) {
-                                       if yes && !canceled {
-                                               saveWithSudo()
-                                               h.completeAction(action)
-                                       }
-                               })
+                               InfoBar.YNPrompt(
+                                       fmt.Sprintf("Permission denied. Do you want to save this file using %s? (y,n)", config.GlobalSettings["sucmd"].(string)),
+                                       func(yes, canceled bool) {
+                                               if yes && !canceled {
+                                                       saveWithSudo()
+                                                       h.completeAction(action)
+                                               }
+                                       },
+                               )
                                return false
                        }
                } else {
@@ -846,21 +892,24 @@ func (h *BufPane) find(useRegex bool) bool {
        if useRegex {
                prompt = "Find (regex): "
        }
-       InfoBar.Prompt(prompt, "", "Find", func(resp string) {
-               // Event callback
-               match, found, _ := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex)
-               if found {
-                       h.Cursor.SetSelectionStart(match[0])
-                       h.Cursor.SetSelectionEnd(match[1])
-                       h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
-                       h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
-                       h.Cursor.GotoLoc(match[1])
-               } else {
-                       h.Cursor.GotoLoc(h.searchOrig)
-                       h.Cursor.ResetSelection()
+       var eventCallback func(resp string)
+       if h.Buf.Settings["incsearch"].(bool) {
+               eventCallback = func(resp string) {
+                       match, found, _ := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex)
+                       if found {
+                               h.Cursor.SetSelectionStart(match[0])
+                               h.Cursor.SetSelectionEnd(match[1])
+                               h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
+                               h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
+                               h.Cursor.GotoLoc(match[1])
+                       } else {
+                               h.Cursor.GotoLoc(h.searchOrig)
+                               h.Cursor.ResetSelection()
+                       }
+                       h.Relocate()
                }
-               h.Relocate()
-       }, func(resp string, canceled bool) {
+       }
+       findCallback := func(resp string, canceled bool) {
                // Finished callback
                if !canceled {
                        match, found, err := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex)
@@ -883,8 +932,15 @@ func (h *BufPane) find(useRegex bool) bool {
                        h.Cursor.ResetSelection()
                }
                h.Relocate()
-       })
-
+       }
+       pattern := string(h.Cursor.GetSelection())
+       if eventCallback != nil && pattern != "" {
+               eventCallback(pattern)
+       }
+       InfoBar.Prompt(prompt, pattern, "Find", eventCallback, findCallback)
+       if pattern != "" {
+               InfoBar.SelectAll()
+       }
        return true
 }
 
@@ -961,32 +1017,24 @@ func (h *BufPane) Redo() bool {
 // Copy the selection to the system clipboard
 func (h *BufPane) Copy() bool {
        if h.Cursor.HasSelection() {
-               h.Cursor.CopySelection("clipboard")
+               h.Cursor.CopySelection(clipboard.ClipboardReg)
                h.freshClip = true
-               if clipboard.Unsupported {
-                       InfoBar.Message("Copied selection (install xclip for external clipboard)")
-               } else {
-                       InfoBar.Message("Copied selection")
-               }
+               InfoBar.Message("Copied selection")
        }
        h.Relocate()
        return true
 }
 
-// Copy the current line to the clipboard
+// CopyLine copies the current line to the clipboard
 func (h *BufPane) CopyLine() bool {
        if h.Cursor.HasSelection() {
                return false
-       } else {
-               h.Cursor.SelectLine()
-               h.Cursor.CopySelection("clipboard")
-               h.freshClip = true
-               if clipboard.Unsupported {
-                       InfoBar.Message("Copied line (install xclip for external clipboard)")
-               } else {
-                       InfoBar.Message("Copied line")
-               }
        }
+       h.Cursor.SelectLine()
+       h.Cursor.CopySelection(clipboard.ClipboardReg)
+       h.freshClip = true
+       InfoBar.Message("Copied line")
+
        h.Cursor.Deselect(true)
        h.Relocate()
        return true
@@ -998,15 +1046,15 @@ func (h *BufPane) CutLine() bool {
        if !h.Cursor.HasSelection() {
                return false
        }
-       if h.freshClip == true {
+       if h.freshClip {
                if h.Cursor.HasSelection() {
-                       if clip, err := clipboard.ReadAll("clipboard"); err != nil {
-                               // messenger.Error(err)
+                       if clip, err := clipboard.Read(clipboard.ClipboardReg); err != nil {
+                               InfoBar.Error(err)
                        } else {
-                               clipboard.WriteAll(clip+string(h.Cursor.GetSelection()), "clipboard")
+                               clipboard.WriteMulti(clip+string(h.Cursor.GetSelection()), clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors())
                        }
                }
-       } else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || h.freshClip == false {
+       } else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || !h.freshClip {
                h.Copy()
        }
        h.freshClip = true
@@ -1021,7 +1069,7 @@ func (h *BufPane) CutLine() bool {
 // Cut the selection to the system clipboard
 func (h *BufPane) Cut() bool {
        if h.Cursor.HasSelection() {
-               h.Cursor.CopySelection("clipboard")
+               h.Cursor.CopySelection(clipboard.ClipboardReg)
                h.Cursor.DeleteSelection()
                h.Cursor.ResetSelection()
                h.freshClip = true
@@ -1029,9 +1077,8 @@ func (h *BufPane) Cut() bool {
 
                h.Relocate()
                return true
-       } else {
-               return h.CutLine()
        }
+       return h.CutLine()
 }
 
 // DuplicateLine duplicates the current line or selection
@@ -1147,16 +1194,24 @@ func (h *BufPane) MoveLinesDown() bool {
 // Paste whatever is in the system clipboard into the buffer
 // Delete and paste if the user has a selection
 func (h *BufPane) Paste() bool {
-       clip, _ := clipboard.ReadAll("clipboard")
-       h.paste(clip)
+       clip, err := clipboard.ReadMulti(clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors())
+       if err != nil {
+               InfoBar.Error(err)
+       } else {
+               h.paste(clip)
+       }
        h.Relocate()
        return true
 }
 
 // PastePrimary pastes from the primary clipboard (only use on linux)
 func (h *BufPane) PastePrimary() bool {
-       clip, _ := clipboard.ReadAll("primary")
-       h.paste(clip)
+       clip, err := clipboard.ReadMulti(clipboard.PrimaryReg, h.Cursor.Num, h.Buf.NumCursors())
+       if err != nil {
+               InfoBar.Error(err)
+       } else {
+               h.paste(clip)
+       }
        h.Relocate()
        return true
 }
@@ -1165,7 +1220,7 @@ func (h *BufPane) paste(clip string) {
        if h.Buf.Settings["smartpaste"].(bool) {
                if h.Cursor.X > 0 && len(util.GetLeadingWhitespace([]byte(strings.TrimLeft(clip, "\r\n")))) == 0 {
                        leadingWS := util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))
-                       clip = strings.Replace(clip, "\n", "\n"+string(leadingWS), -1)
+                       clip = strings.ReplaceAll(clip, "\n", "\n"+string(leadingWS))
                }
        }
 
@@ -1177,11 +1232,7 @@ func (h *BufPane) paste(clip string) {
        h.Buf.Insert(h.Cursor.Loc, clip)
        // h.Cursor.Loc = h.Cursor.Loc.Move(Count(clip), h.Buf)
        h.freshClip = false
-       if clipboard.Unsupported {
-               InfoBar.Message("Pasted clipboard (install xclip for external clipboard)")
-       } else {
-               InfoBar.Message("Pasted clipboard")
-       }
+       InfoBar.Message("Pasted clipboard")
 }
 
 // JumpToMatchingBrace moves the cursor to the matching brace if it is
@@ -1198,6 +1249,7 @@ func (h *BufPane) JumpToMatchingBrace() bool {
                                } else {
                                        h.Cursor.GotoLoc(matchingBrace.Move(1, h.Buf))
                                }
+                               break
                        } else {
                                return false
                        }
@@ -1242,45 +1294,29 @@ func (h *BufPane) JumpLine() bool {
 // Start moves the viewport to the start of the buffer
 func (h *BufPane) Start() bool {
        v := h.GetView()
-       v.StartLine = 0
+       v.StartLine = display.SLoc{0, 0}
        h.SetView(v)
        return true
 }
 
 // End moves the viewport to the end of the buffer
 func (h *BufPane) End() bool {
-       // TODO: softwrap problems?
        v := h.GetView()
-       if v.Height > h.Buf.LinesNum() {
-               v.StartLine = 0
-               h.SetView(v)
-       } else {
-               v.StartLine = h.Buf.LinesNum() - v.Height
-               h.SetView(v)
-       }
+       v.StartLine = h.Scroll(h.SLocFromLoc(h.Buf.End()), -h.BufView().Height+1)
+       h.SetView(v)
        return true
 }
 
 // PageUp scrolls the view up a page
 func (h *BufPane) PageUp() bool {
-       v := h.GetView()
-       if v.StartLine > v.Height {
-               h.ScrollUp(v.Height)
-       } else {
-               v.StartLine = 0
-       }
-       h.SetView(v)
+       h.ScrollUp(h.BufView().Height)
        return true
 }
 
 // PageDown scrolls the view down a page
 func (h *BufPane) PageDown() bool {
-       v := h.GetView()
-       if h.Buf.LinesNum()-(v.StartLine+v.Height) > v.Height {
-               h.ScrollDown(v.Height)
-       } else if h.Buf.LinesNum() >= v.Height {
-               v.StartLine = h.Buf.LinesNum() - v.Height
-       }
+       h.ScrollDown(h.BufView().Height)
+       h.ScrollAdjust()
        return true
 }
 
@@ -1289,7 +1325,7 @@ func (h *BufPane) SelectPageUp() bool {
        if !h.Cursor.HasSelection() {
                h.Cursor.OrigSelection[0] = h.Cursor.Loc
        }
-       h.Cursor.UpN(h.GetView().Height)
+       h.MoveCursorUp(h.BufView().Height)
        h.Cursor.SelectTo(h.Cursor.Loc)
        h.Relocate()
        return true
@@ -1300,7 +1336,7 @@ func (h *BufPane) SelectPageDown() bool {
        if !h.Cursor.HasSelection() {
                h.Cursor.OrigSelection[0] = h.Cursor.Loc
        }
-       h.Cursor.DownN(h.GetView().Height)
+       h.MoveCursorDown(h.BufView().Height)
        h.Cursor.SelectTo(h.Cursor.Loc)
        h.Relocate()
        return true
@@ -1315,7 +1351,7 @@ func (h *BufPane) CursorPageUp() bool {
                h.Cursor.ResetSelection()
                h.Cursor.StoreVisualX()
        }
-       h.Cursor.UpN(h.GetView().Height)
+       h.MoveCursorUp(h.BufView().Height)
        h.Relocate()
        return true
 }
@@ -1329,34 +1365,21 @@ func (h *BufPane) CursorPageDown() bool {
                h.Cursor.ResetSelection()
                h.Cursor.StoreVisualX()
        }
-       h.Cursor.DownN(h.GetView().Height)
+       h.MoveCursorDown(h.BufView().Height)
        h.Relocate()
        return true
 }
 
 // HalfPageUp scrolls the view up half a page
 func (h *BufPane) HalfPageUp() bool {
-       v := h.GetView()
-       if v.StartLine > v.Height/2 {
-               h.ScrollUp(v.Height / 2)
-       } else {
-               v.StartLine = 0
-       }
-       h.SetView(v)
+       h.ScrollUp(h.BufView().Height / 2)
        return true
 }
 
 // HalfPageDown scrolls the view down half a page
 func (h *BufPane) HalfPageDown() bool {
-       v := h.GetView()
-       if h.Buf.LinesNum()-(v.StartLine+v.Height) > v.Height/2 {
-               h.ScrollDown(v.Height / 2)
-       } else {
-               if h.Buf.LinesNum() >= v.Height {
-                       v.StartLine = h.Buf.LinesNum() - v.Height
-               }
-       }
-       h.SetView(v)
+       h.ScrollDown(h.BufView().Height / 2)
+       h.ScrollAdjust()
        return true
 }
 
@@ -1455,39 +1478,43 @@ func (h *BufPane) ClearInfo() bool {
        return true
 }
 
+// ForceQuit closes the current tab or view even if there are unsaved changes
+// (no prompt)
+func (h *BufPane) ForceQuit() bool {
+       h.Buf.Close()
+       if len(MainTab().Panes) > 1 {
+               h.Unsplit()
+       } else if len(Tabs.List) > 1 {
+               Tabs.RemoveTab(h.splitID)
+       } else {
+               screen.Screen.Fini()
+               InfoBar.Close()
+               runtime.Goexit()
+       }
+       return true
+}
+
 // Quit this will close the current tab or view that is open
 func (h *BufPane) Quit() bool {
-       quit := func() {
-               h.Buf.Close()
-               if len(MainTab().Panes) > 1 {
-                       h.Unsplit()
-               } else if len(Tabs.List) > 1 {
-                       Tabs.RemoveTab(h.splitID)
-               } else {
-                       screen.Screen.Fini()
-                       InfoBar.Close()
-                       runtime.Goexit()
-               }
-       }
        if h.Buf.Modified() {
                if config.GlobalSettings["autosave"].(float64) > 0 {
                        // autosave on means we automatically save when quitting
                        h.SaveCB("Quit", func() {
-                               quit()
+                               h.ForceQuit()
                        })
                } else {
                        InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) {
                                if !canceled && !yes {
-                                       quit()
+                                       h.ForceQuit()
                                } else if !canceled && yes {
                                        h.SaveCB("Quit", func() {
-                                               quit()
+                                               h.ForceQuit()
                                        })
                                }
                        })
                }
        } else {
-               quit()
+               h.ForceQuit()
        }
        return true
 }
@@ -1610,12 +1637,12 @@ func (h *BufPane) PreviousSplit() bool {
 }
 
 var curmacro []interface{}
-var recording_macro bool
+var recordingMacro bool
 
 // ToggleMacro toggles recording of a macro
 func (h *BufPane) ToggleMacro() bool {
-       recording_macro = !recording_macro
-       if recording_macro {
+       recordingMacro = !recordingMacro
+       if recordingMacro {
                curmacro = []interface{}{}
                InfoBar.Message("Recording")
        } else {
@@ -1627,7 +1654,7 @@ func (h *BufPane) ToggleMacro() bool {
 
 // PlayMacro plays back the most recently recorded macro
 func (h *BufPane) PlayMacro() bool {
-       if recording_macro {
+       if recordingMacro {
                return false
        }
        for _, action := range curmacro {
@@ -1687,10 +1714,9 @@ func (h *BufPane) SpawnMultiCursor() bool {
 func (h *BufPane) SpawnMultiCursorUp() bool {
        if h.Cursor.Y == 0 {
                return false
-       } else {
-               h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
-               h.Cursor.Relocate()
        }
+       h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
+       h.Cursor.Relocate()
 
        c := buffer.NewCursor(h.Buf, buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
        h.Buf.AddCursor(c)
@@ -1705,10 +1731,9 @@ func (h *BufPane) SpawnMultiCursorUp() bool {
 func (h *BufPane) SpawnMultiCursorDown() bool {
        if h.Cursor.Y+1 == h.Buf.LinesNum() {
                return false
-       } else {
-               h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
-               h.Cursor.Relocate()
        }
+       h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
+       h.Cursor.Relocate()
 
        c := buffer.NewCursor(h.Buf, buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
        h.Buf.AddCursor(c)