From 06d3d2c130e0d4c34c2baba83dadc816c12cec5e Mon Sep 17 00:00:00 2001 From: delthas Date: Wed, 3 Jul 2024 17:00:16 +0200 Subject: [PATCH] Add CTRL+K as a shortcut to buffer; filter buffer view by name This makes CTRL+K behave as a real channel switcher, akin to Discord's CTRL+K. Fixes: https://todo.sr.ht/~delthas/senpai/83 --- app.go | 12 ++++-- commands.go | 4 +- doc/senpai.1.scd | 9 ++++- ui/buffers.go | 101 +++++++++++++++++++++++++++++++++++++++-------- ui/ui.go | 18 ++++++--- window.go | 33 +++++++++++----- 6 files changed, 138 insertions(+), 39 deletions(-) diff --git a/app.go b/app.go index cbf952c..ea96281 100644 --- a/app.go +++ b/app.go @@ -546,7 +546,7 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) { app.win.ClickChannelCol(true) app.win.SetMouseShape(vaxis.MouseShapeResizeHorizontal) } else if x < app.win.ChannelWidth() { - app.win.ClickBuffer(y + app.win.ChannelOffset()) + app.win.ClickBuffer(app.win.VerticalBufferOffset(y)) } else if app.win.ChannelWidth() == 0 && y == h-1 { app.win.ClickBuffer(app.win.HorizontalBufferOffset(x)) } else if x == w-app.win.MemberWidth() { @@ -561,7 +561,7 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) { if ev.Button == vaxis.MouseMiddleButton { i := -1 if x < app.win.ChannelWidth() { - i = y + app.win.ChannelOffset() + i = app.win.VerticalBufferOffset(y) } else if app.win.ChannelWidth() == 0 && y == h-1 { i = app.win.HorizontalBufferOffset(x) } @@ -578,12 +578,14 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) { } if ev.EventType == vaxis.EventRelease { if x < app.win.ChannelWidth()-1 { - if i := y + app.win.ChannelOffset(); i == app.win.ClickedBuffer() { + if i := app.win.VerticalBufferOffset(y); i == app.win.ClickedBuffer() { app.win.GoToBufferNo(i) + app.clearBufferCommand() } } else if app.win.ChannelWidth() == 0 && y == h-1 { if i := app.win.HorizontalBufferOffset(x); i >= 0 && i == app.win.ClickedBuffer() { app.win.GoToBufferNo(i) + app.clearBufferCommand() } } else if x > w-app.win.MemberWidth() { if i := y - 2 + app.win.MemberOffset(); i >= 0 && i == app.win.ClickedMember() { @@ -680,6 +682,10 @@ func (app *App) handleKeyEvent(ev vaxis.Key) { if len(app.win.InputContent()) == 0 { app.win.InputSet("/search ") } + } else if keyMatches(ev, 'k', vaxis.ModCtrl) { + if len(app.win.InputContent()) == 0 { + app.win.InputSet("/buffer ") + } } else if keyMatches(ev, 'a', vaxis.ModCtrl) { app.win.InputHome() } else if keyMatches(ev, 'e', vaxis.ModCtrl) { diff --git a/commands.go b/commands.go index 40fe0d5..4784e04 100644 --- a/commands.go +++ b/commands.go @@ -165,8 +165,8 @@ func init() { AllowHome: true, MinArgs: 1, MaxArgs: 1, - Usage: "", - Desc: "switch to the buffer containing a substring", + Usage: "", + Desc: "switch to the buffer at the position or containing a substring", Handle: commandDoBuffer, }, "WHOIS": { diff --git a/doc/senpai.1.scd b/doc/senpai.1.scd index 2217c35..8f3ce8e 100644 --- a/doc/senpai.1.scd +++ b/doc/senpai.1.scd @@ -99,6 +99,9 @@ will be shown, which can be closed by clicking again anywhere in the interface. *CTRL-F* Prepare for search: add /search to input line. +*CTRL-K* + Prepare for jumping to a buffer: add /buffer to input line. + *CTRL-U*, *PgUp* Go up in the timeline. @@ -206,8 +209,10 @@ _name_ is matched case-insensitively. It can be one of the following: *LIST* [pattern] List public channels, optionally matching the specified pattern. -*BUFFER* - Switch to the buffer containing _name_. +*BUFFER* + Switch to the buffer at the _index_ position, or containing _name_. + The buffer list will be filtered according to the passed name; entering the + command will select the first buffer in the list. *WHOIS* Get information about someone who is connected. diff --git a/ui/buffers.go b/ui/buffers.go index 62373e3..8cbd02b 100644 --- a/ui/buffers.go +++ b/ui/buffers.go @@ -234,7 +234,8 @@ type BufferList struct { tlHeight int textWidth int - showBufferNumbers bool + filterBuffers bool + filterBuffersQuery string // lowercased } // NewBufferList returns a new BufferList. @@ -298,8 +299,9 @@ func (bs *BufferList) To(i int) bool { return false } -func (bs *BufferList) ShowBufferNumbers(enabled bool) { - bs.showBufferNumbers = enabled +func (bs *BufferList) FilterBuffers(enable bool, query string) { + bs.filterBuffers = enable + bs.filterBuffersQuery = strings.ToLower(query) } func (bs *BufferList) Next() { @@ -684,16 +686,20 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in *offset = 0 } } + off := bs.VerticalBufferOffset(0, *offset) + if off < 0 { + off = len(bs.list) + } width-- drawVerticalLine(vx, x0+width, y0, height) clearArea(vx, x0, y0, width, height) indexPadding := 1 + int(math.Ceil(math.Log10(float64(len(bs.list))))) - for i, b := range bs.list[*offset:] { - bi := *offset + i + y := y0 + for i, b := range bs.list[off:] { + bi := off + i x := x0 - y := y0 + i var st vaxis.Style if b.unread { st.Attribute |= vaxis.AttrBold @@ -702,7 +708,18 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in if bi == bs.current || bi == bs.clicked { st.Attribute |= vaxis.AttrReverse } - if bs.showBufferNumbers { + + var title string + if b.title == "" { + title = b.netName + } else { + title = b.title + } + + if bs.filterBuffers { + if !strings.Contains(strings.ToLower(title), bs.filterBuffersQuery) { + continue + } indexSt := st indexSt.Foreground = ColorGray indexText := fmt.Sprintf("%d:", bi+1) @@ -710,10 +727,7 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in x = x0 + indexPadding } - var title string - if b.title == "" { - title = b.netName - } else { + if b.title != "" { if bi == bs.current || bi == bs.clicked { st := vaxis.Style{ Attribute: vaxis.AttrReverse, @@ -722,7 +736,6 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in setCell(vx, x+1, y, ' ', st) } x += 2 - title = b.title } title = truncate(vx, title, width-(x-x0), "\u2026") printString(vx, &x, y, Styled(title, st)) @@ -746,11 +759,28 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in x = x0 + width - len(highlightText) printString(vx, &x, y, Styled(highlightText, highlightSt)) } + + y++ } } func (bs *BufferList) HorizontalBufferOffset(x int, offset int) int { - for i, b := range bs.list[offset:] { + if bs.filterBuffers { + offset = 0 + } + i := 0 + for bi, b := range bs.list[offset:] { + if bs.filterBuffers { + var title string + if b.title == "" { + title = b.netName + } else { + title = b.title + } + if !strings.Contains(strings.ToLower(title), bs.filterBuffersQuery) { + continue + } + } if i > 0 { x-- if x < 0 { @@ -759,8 +789,35 @@ func (bs *BufferList) HorizontalBufferOffset(x int, offset int) int { } x -= bs.bufferWidth(&b) if x < 0 { - return offset + i + return offset + bi + } + i++ + } + return -1 +} + +func (bs *BufferList) VerticalBufferOffset(y int, offset int) int { + if !bs.filterBuffers { + return offset + y + } + + for i, b := range bs.list { + var title string + if b.title == "" { + title = b.netName + } else { + title = b.title } + + if bs.filterBuffers { + if !strings.Contains(strings.ToLower(title), bs.filterBuffersQuery) { + continue + } + } + if y == 0 { + return i + } + y-- } return -1 } @@ -814,8 +871,13 @@ func (bs *BufferList) DrawHorizontalBufferList(vx *Vaxis, x0, y0, width int, off } x = x0 - for i, b := range bs.list[*offset:] { - i := i + *offset + off := bs.HorizontalBufferOffset(0, *offset) + if off < 0 { + off = len(bs.list) + } + + for i, b := range bs.list[off:] { + i := i + off if width <= x-x0 { break } @@ -837,6 +899,13 @@ func (bs *BufferList) DrawHorizontalBufferList(vx *Vaxis, x0, y0, width int, off } else { title = b.title } + + if bs.filterBuffers { + if !strings.Contains(strings.ToLower(title), bs.filterBuffersQuery) { + continue + } + } + title = truncate(vx, title, width-x, "\u2026") printString(vx, &x, y0, Styled(title, st)) diff --git a/ui/ui.go b/ui/ui.go index fd198d0..f6aefc7 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -174,7 +174,7 @@ func (ui *UI) ClickedBuffer() int { } func (ui *UI) ClickBuffer(i int) { - if i < len(ui.bs.list) { + if i >= 0 && i < len(ui.bs.list) { ui.bs.clicked = i } } @@ -221,8 +221,8 @@ func (ui *UI) GoToBufferNo(i int) { } } -func (ui *UI) ShowBufferNumbers(enable bool) { - ui.bs.ShowBufferNumbers(enable) +func (ui *UI) FilterBuffers(enable bool, query string) { + ui.bs.FilterBuffers(enable, query) } func (ui *UI) ClickedMember() int { @@ -284,8 +284,8 @@ func (ui *UI) HorizontalBufferOffset(x int) int { return ui.bs.HorizontalBufferOffset(x, ui.channelOffset) } -func (ui *UI) ChannelOffset() int { - return ui.channelOffset +func (ui *UI) VerticalBufferOffset(y int) int { + return ui.bs.VerticalBufferOffset(y, ui.channelOffset) } func (ui *UI) MemberOffset() int { @@ -393,7 +393,13 @@ func (ui *UI) AddLines(netID, buffer string, before, after []Line) { func (ui *UI) JumpBuffer(sub string) bool { subLower := strings.ToLower(sub) for i, b := range ui.bs.list { - if strings.Contains(strings.ToLower(b.title), subLower) { + var title string + if b.title == "" { + title = b.netName + } else { + title = b.title + } + if strings.Contains(strings.ToLower(title), subLower) { if ui.bs.To(i) { ui.memberOffset = 0 } diff --git a/window.go b/window.go index 50bc396..816d91b 100644 --- a/window.go +++ b/window.go @@ -1,6 +1,7 @@ package senpai import ( + "strconv" "strings" "time" @@ -79,17 +80,29 @@ func (app *App) setStatus() { func (app *App) setBufferNumbers() { input := app.win.InputContent() if !isCommand(input) { - app.win.ShowBufferNumbers(false) + app.win.FilterBuffers(false, "") return } - commandEnd := len(input) - for i := 1; i < len(input); i++ { - if input[i] == ' ' { - commandEnd = i - break - } + cmd, arg, _ := strings.Cut(string(input[1:]), " ") + if cmd == "" || !strings.HasPrefix("buffer", cmd) { + app.win.FilterBuffers(false, "") + return + } + if _, err := strconv.Atoi(arg); err == nil { + // Do not filter buffers if we are passing a buffer index + arg = "" + } + app.win.FilterBuffers(true, arg) +} + +func (app *App) clearBufferCommand() { + input := app.win.InputContent() + if !isCommand(input) { + return + } + cmd, _, _ := strings.Cut(string(input[1:]), " ") + if cmd == "" || !strings.HasPrefix("buffer", cmd) { + return } - command := string(input[1:commandEnd]) - showBufferNumbers := len(command) != 0 && strings.HasPrefix("buffer", command) - app.win.ShowBufferNumbers(showBufferNumbers) + app.win.InputClear() } -- 2.45.2