]> git.lizzy.rs Git - micro.git/blobdiff - internal/action/command.go
Merge
[micro.git] / internal / action / command.go
index 724a8d69edd77045e1121fe1e0c2c1a12e889166..f3f5db483f78002fb618545e981540e270b23626 100644 (file)
@@ -10,14 +10,14 @@ import (
        "regexp"
        "strconv"
        "strings"
-       "unicode/utf8"
 
        shellquote "github.com/kballard/go-shellquote"
-       "github.com/zyedidia/micro/internal/buffer"
-       "github.com/zyedidia/micro/internal/config"
-       "github.com/zyedidia/micro/internal/screen"
-       "github.com/zyedidia/micro/internal/shell"
-       "github.com/zyedidia/micro/internal/util"
+       "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/screen"
+       "github.com/zyedidia/micro/v2/internal/shell"
+       "github.com/zyedidia/micro/v2/internal/util"
 )
 
 // A Command contains information about how to execute a command
@@ -56,6 +56,7 @@ func InitCommands() {
                "cd":         {(*BufPane).CdCmd, buffer.FileComplete},
                "pwd":        {(*BufPane).PwdCmd, nil},
                "open":       {(*BufPane).OpenCmd, buffer.FileComplete},
+               "tabmove":    {(*BufPane).TabMoveCmd, nil},
                "tabswitch":  {(*BufPane).TabSwitchCmd, nil},
                "term":       {(*BufPane).TermCmd, nil},
                "memusage":   {(*BufPane).MemUsageCmd, nil},
@@ -106,7 +107,7 @@ func (h *BufPane) PluginCmd(args []string) {
        }
 
        if h.Buf.Type != buffer.BTLog {
-               OpenLogBuf(h)
+               h.OpenLogBuf()
        }
 
        config.PluginCommand(buffer.LogBuf, args[0], args[1:])
@@ -155,6 +156,56 @@ func (h *BufPane) TextFilterCmd(args []string) {
        h.Buf.Insert(h.Cursor.Loc, bout.String())
 }
 
+// TabMoveCmd moves the current tab to a given index (starts at 1). The
+// displaced tabs are moved up.
+func (h *BufPane) TabMoveCmd(args []string) {
+       if len(args) <= 0 {
+               InfoBar.Error("Not enough arguments: provide an index, starting at 1")
+               return
+       }
+
+       if len(args[0]) <= 0 {
+               InfoBar.Error("Invalid argument: empty string")
+               return
+       }
+
+       num, err := strconv.Atoi(args[0])
+       if err != nil {
+               InfoBar.Error("Invalid argument: ", err)
+               return
+       }
+
+       // Preserve sign for relative move, if one exists
+       var shiftDirection byte
+       if strings.Contains("-+", string([]byte{args[0][0]})) {
+               shiftDirection = args[0][0]
+       }
+
+       // Relative positions -> absolute positions
+       idxFrom := Tabs.Active()
+       idxTo := 0
+       offset := util.Abs(num)
+       if shiftDirection == '-' {
+               idxTo = idxFrom - offset
+       } else if shiftDirection == '+' {
+               idxTo = idxFrom + offset
+       } else {
+               idxTo = offset - 1
+       }
+
+       // Restrain position to within the valid range
+       idxTo = util.Clamp(idxTo, 0, len(Tabs.List)-1)
+
+       activeTab := Tabs.List[idxFrom]
+       Tabs.RemoveTab(activeTab.ID())
+       Tabs.List = append(Tabs.List, nil)
+       copy(Tabs.List[idxTo+1:], Tabs.List[idxTo:])
+       Tabs.List[idxTo] = activeTab
+       Tabs.UpdateNames()
+       Tabs.SetActive(idxTo)
+       // InfoBar.Message(fmt.Sprintf("Moved tab from slot %d to %d", idxFrom+1, idxTo+1))
+}
+
 // TabSwitchCmd switches to a given tab either by name or by number
 func (h *BufPane) TabSwitchCmd(args []string) {
        if len(args) > 0 {
@@ -272,7 +323,7 @@ func (h *BufPane) OpenCmd(args []string) {
 // ToggleLogCmd toggles the log view
 func (h *BufPane) ToggleLogCmd(args []string) {
        if h.Buf.Type != buffer.BTLog {
-               OpenLogBuf(h)
+               h.OpenLogBuf()
        } else {
                h.Quit()
        }
@@ -289,7 +340,10 @@ func ReloadConfig() {
        if err != nil {
                screen.TermMessage(err)
        }
-       config.InitGlobalSettings()
+       err = config.InitGlobalSettings()
+       if err != nil {
+               screen.TermMessage(err)
+       }
        InitBindings()
        InitCommands()
 
@@ -427,6 +481,7 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error {
 
        if !local {
                config.GlobalSettings[option] = nativeValue
+               config.ModifiedSettings[option] = true
 
                if option == "colorscheme" {
                        // LoadSyntaxFiles()
@@ -451,6 +506,12 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error {
                        }
                } else if option == "paste" {
                        screen.Screen.SetPaste(nativeValue.(bool))
+               } else if option == "clipboard" {
+                       m := clipboard.SetMethod(nativeValue.(string))
+                       err := clipboard.Initialize(m)
+                       if err != nil {
+                               return err
+                       }
                } else {
                        for _, pl := range config.Plugins {
                                if option == pl.Name {
@@ -475,7 +536,7 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error {
                b.SetOptionNative(option, nativeValue)
        }
 
-       return config.WriteSettings(config.ConfigDir + "/settings.json")
+       return config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json"))
 }
 
 func SetGlobalOption(option, value string) error {
@@ -651,8 +712,11 @@ func (h *BufPane) GotoCmd(args []string) {
                                InfoBar.Error(err)
                                return
                        }
+                       if line < 0 {
+                               line = h.Buf.LinesNum() + 1 + line
+                       }
                        line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
-                       col = util.Clamp(col-1, 0, utf8.RuneCount(h.Buf.LineBytes(line)))
+                       col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
                        h.Cursor.GotoLoc(buffer.Loc{col, line})
                } else {
                        line, err := strconv.Atoi(args[0])
@@ -660,6 +724,9 @@ func (h *BufPane) GotoCmd(args []string) {
                                InfoBar.Error(err)
                                return
                        }
+                       if line < 0 {
+                               line = h.Buf.LinesNum() + 1 + line
+                       }
                        line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
                        h.Cursor.GotoLoc(buffer.Loc{0, line})
                }
@@ -732,23 +799,23 @@ func (h *BufPane) ReplaceCmd(args []string) {
 
        nreplaced := 0
        start := h.Buf.Start()
-       // end := h.Buf.End()
-       // if h.Cursor.HasSelection() {
-       //      start = h.Cursor.CurSelection[0]
-       //      end = h.Cursor.CurSelection[1]
-       // }
+       end := h.Buf.End()
+       selection := h.Cursor.HasSelection()
+       if selection {
+               start = h.Cursor.CurSelection[0]
+               end = h.Cursor.CurSelection[1]
+       }
        if all {
-               nreplaced = h.Buf.ReplaceRegex(start, h.Buf.End(), regex, replace)
+               nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace)
        } else {
                inRange := func(l buffer.Loc) bool {
-                       return l.GreaterEqual(start) && l.LessEqual(h.Buf.End())
+                       return l.GreaterEqual(start) && l.LessEqual(end)
                }
 
-               searchLoc := start
-               searching := true
+               searchLoc := h.Cursor.Loc
                var doReplacement func()
                doReplacement = func() {
-                       locs, found, err := h.Buf.FindNext(search, start, h.Buf.End(), searchLoc, true, !noRegex)
+                       locs, found, err := h.Buf.FindNext(search, start, end, searchLoc, true, !noRegex)
                        if err != nil {
                                InfoBar.Error(err)
                                return
@@ -756,45 +823,58 @@ func (h *BufPane) ReplaceCmd(args []string) {
                        if !found || !inRange(locs[0]) || !inRange(locs[1]) {
                                h.Cursor.ResetSelection()
                                h.Buf.RelocateCursors()
+
                                return
                        }
 
                        h.Cursor.SetSelectionStart(locs[0])
                        h.Cursor.SetSelectionEnd(locs[1])
+                       h.Cursor.GotoLoc(locs[0])
+
+                       h.Relocate()
 
                        InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) {
                                if !canceled && yes {
-                                       h.Buf.Replace(locs[0], locs[1], replaceStr)
+                                       _, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace)
 
                                        searchLoc = locs[0]
-                                       searchLoc.X += utf8.RuneCount(replace)
+                                       searchLoc.X += nrunes + locs[0].Diff(locs[1], h.Buf)
+                                       if end.Y == locs[1].Y {
+                                               end = end.Move(nrunes, h.Buf)
+                                       }
                                        h.Cursor.Loc = searchLoc
                                        nreplaced++
                                } else if !canceled && !yes {
                                        searchLoc = locs[0]
-                                       searchLoc.X += utf8.RuneCount(replace)
+                                       searchLoc.X += util.CharacterCount(replace)
                                } else if canceled {
                                        h.Cursor.ResetSelection()
                                        h.Buf.RelocateCursors()
                                        return
                                }
-                               if searching {
-                                       doReplacement()
-                               }
+                               doReplacement()
                        })
                }
                doReplacement()
        }
 
        h.Buf.RelocateCursors()
+       h.Relocate()
 
+       var s string
        if nreplaced > 1 {
-               InfoBar.Message("Replaced ", nreplaced, " occurrences of ", search)
+               s = fmt.Sprintf("Replaced %d occurrences of %s", nreplaced, search)
        } else if nreplaced == 1 {
-               InfoBar.Message("Replaced ", nreplaced, " occurrence of ", search)
+               s = fmt.Sprintf("Replaced 1 occurrence of %s", search)
        } else {
-               InfoBar.Message("Nothing matched ", search)
+               s = fmt.Sprintf("Nothing matched %s", search)
        }
+
+       if selection {
+               s += " in selection"
+       }
+
+       InfoBar.Message(s)
 }
 
 // ReplaceAllCmd replaces search term all at once
@@ -807,6 +887,11 @@ func (h *BufPane) ReplaceAllCmd(args []string) {
 func (h *BufPane) TermCmd(args []string) {
        ps := h.tab.Panes
 
+       if !TermEmuSupported {
+               InfoBar.Error("Terminal emulator not supported on this system")
+               return
+       }
+
        if len(args) == 0 {
                sh := os.Getenv("SHELL")
                if sh == "" {
@@ -818,7 +903,11 @@ func (h *BufPane) TermCmd(args []string) {
 
        term := func(i int, newtab bool) {
                t := new(shell.Terminal)
-               t.Start(args, false, true, nil, nil)
+               err := t.Start(args, false, true, nil, nil)
+               if err != nil {
+                       InfoBar.Error(err)
+                       return
+               }
 
                id := h.ID()
                if newtab {
@@ -830,7 +919,12 @@ func (h *BufPane) TermCmd(args []string) {
                }
 
                v := h.GetView()
-               MainTab().Panes[i] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
+               tp, err := NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
+               if err != nil {
+                       InfoBar.Error(err)
+                       return
+               }
+               MainTab().Panes[i] = tp
                MainTab().SetActive(i)
        }