diff --git a/commands/move-tab.go b/commands/move-tab.go new file mode 100644 index 0000000..9f0293c --- /dev/null +++ b/commands/move-tab.go @@ -0,0 +1,51 @@ +package commands + +import ( + "fmt" + "strconv" + "strings" + + "git.sr.ht/~sircmpwn/aerc/widgets" +) + +type MoveTab struct{} + +func init() { + register(MoveTab{}) +} + +func (MoveTab) Aliases() []string { + return []string{"move-tab"} +} + +func (MoveTab) Complete(aerc *widgets.Aerc, args []string) []string { + return nil +} + +func (MoveTab) Execute(aerc *widgets.Aerc, args []string) error { + if len(args) == 1 { + return fmt.Errorf("Usage: %s [+|-]", args[0]) + } + + joinedArgs := strings.Join(args[1:], "") + + n, err := strconv.Atoi(joinedArgs) + if err != nil { + return fmt.Errorf("failed to parse index argument: %v", err) + } + + i := aerc.SelectedTabIndex() + l := aerc.NumTabs() + + if strings.HasPrefix(joinedArgs, "+") { + i = (i + n) % l + } else if strings.HasPrefix(joinedArgs, "-") { + i = (((i + n) % l) + l) % l + } else { + i = n + } + + aerc.MoveTab(i) + + return nil +} diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd index 38c0bd4..0294784 100644 --- a/doc/aerc.1.scd +++ b/doc/aerc.1.scd @@ -63,6 +63,10 @@ These commands work in any context. Opens a new terminal tab with a shell running in the current working directory, or the specified command. +*move-tab* [+|-] + Moves the selected tab to the given index. If + or - is specified, the + number is interpreted as a delta from the selected tab. + *prev-tab* [n], *next-tab* [n] Cycles to the previous or next tab in the list, repeating n times (default: 1). diff --git a/lib/ui/tab.go b/lib/ui/tab.go index 27fb604..e1c53ea 100644 --- a/lib/ui/tab.go +++ b/lib/ui/tab.go @@ -132,6 +132,47 @@ func (tabs *Tabs) SelectPrevious() bool { return true } +func (tabs *Tabs) MoveTab(to int) { + from := tabs.Selected + + if to < 0 { + to = 0 + } + + if to >= len(tabs.Tabs) { + to = len(tabs.Tabs) - 1 + } + + tab := tabs.Tabs[from] + if to > from { + copy(tabs.Tabs[from:to], tabs.Tabs[from+1:to+1]) + for i, h := range tabs.history { + if h == from { + tabs.history[i] = to + } + if h > from && h <= to { + tabs.history[i] -= 1 + } + } + } else if from > to { + copy(tabs.Tabs[to+1:from+1], tabs.Tabs[to:from]) + for i, h := range tabs.history { + if h == from { + tabs.history[i] = to + } + if h >= to && h < from { + tabs.history[i] += 1 + } + } + } else { + return + } + + tabs.Tabs[to] = tab + tabs.Selected = to + tabs.TabStrip.Invalidate() +} + func (tabs *Tabs) NextTab() { next := tabs.Selected + 1 if next >= len(tabs.Tabs) { diff --git a/widgets/aerc.go b/widgets/aerc.go index a0e356a..a9be47e 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -283,6 +283,14 @@ func (aerc *Aerc) SelectedTab() ui.Drawable { return aerc.tabs.Tabs[aerc.tabs.Selected].Content } +func (aerc *Aerc) SelectedTabIndex() int { + return aerc.tabs.Selected +} + +func (aerc *Aerc) NumTabs() int { + return len(aerc.tabs.Tabs) +} + func (aerc *Aerc) NewTab(clickable ui.Drawable, name string) *ui.Tab { tab := aerc.tabs.Add(clickable, name) aerc.tabs.Select(len(aerc.tabs.Tabs) - 1) @@ -297,6 +305,10 @@ func (aerc *Aerc) ReplaceTab(tabSrc ui.Drawable, tabTarget ui.Drawable, name str aerc.tabs.Replace(tabSrc, tabTarget, name) } +func (aerc *Aerc) MoveTab(i int) { + aerc.tabs.MoveTab(i) +} + func (aerc *Aerc) NextTab() { aerc.tabs.NextTab() }