commit 06d3d2c
delthas
·
2024-07-03 15:00:16 +0000 UTC
parent 1a988df
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
6 files changed,
+138,
-39
M
app.go
+9,
-3
1@@ -546,7 +546,7 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) {
2 app.win.ClickChannelCol(true)
3 app.win.SetMouseShape(vaxis.MouseShapeResizeHorizontal)
4 } else if x < app.win.ChannelWidth() {
5- app.win.ClickBuffer(y + app.win.ChannelOffset())
6+ app.win.ClickBuffer(app.win.VerticalBufferOffset(y))
7 } else if app.win.ChannelWidth() == 0 && y == h-1 {
8 app.win.ClickBuffer(app.win.HorizontalBufferOffset(x))
9 } else if x == w-app.win.MemberWidth() {
10@@ -561,7 +561,7 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) {
11 if ev.Button == vaxis.MouseMiddleButton {
12 i := -1
13 if x < app.win.ChannelWidth() {
14- i = y + app.win.ChannelOffset()
15+ i = app.win.VerticalBufferOffset(y)
16 } else if app.win.ChannelWidth() == 0 && y == h-1 {
17 i = app.win.HorizontalBufferOffset(x)
18 }
19@@ -578,12 +578,14 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) {
20 }
21 if ev.EventType == vaxis.EventRelease {
22 if x < app.win.ChannelWidth()-1 {
23- if i := y + app.win.ChannelOffset(); i == app.win.ClickedBuffer() {
24+ if i := app.win.VerticalBufferOffset(y); i == app.win.ClickedBuffer() {
25 app.win.GoToBufferNo(i)
26+ app.clearBufferCommand()
27 }
28 } else if app.win.ChannelWidth() == 0 && y == h-1 {
29 if i := app.win.HorizontalBufferOffset(x); i >= 0 && i == app.win.ClickedBuffer() {
30 app.win.GoToBufferNo(i)
31+ app.clearBufferCommand()
32 }
33 } else if x > w-app.win.MemberWidth() {
34 if i := y - 2 + app.win.MemberOffset(); i >= 0 && i == app.win.ClickedMember() {
35@@ -680,6 +682,10 @@ func (app *App) handleKeyEvent(ev vaxis.Key) {
36 if len(app.win.InputContent()) == 0 {
37 app.win.InputSet("/search ")
38 }
39+ } else if keyMatches(ev, 'k', vaxis.ModCtrl) {
40+ if len(app.win.InputContent()) == 0 {
41+ app.win.InputSet("/buffer ")
42+ }
43 } else if keyMatches(ev, 'a', vaxis.ModCtrl) {
44 app.win.InputHome()
45 } else if keyMatches(ev, 'e', vaxis.ModCtrl) {
+2,
-2
1@@ -165,8 +165,8 @@ func init() {
2 AllowHome: true,
3 MinArgs: 1,
4 MaxArgs: 1,
5- Usage: "<name>",
6- Desc: "switch to the buffer containing a substring",
7+ Usage: "<index|name>",
8+ Desc: "switch to the buffer at the position or containing a substring",
9 Handle: commandDoBuffer,
10 },
11 "WHOIS": {
+7,
-2
1@@ -99,6 +99,9 @@ will be shown, which can be closed by clicking again anywhere in the interface.
2 *CTRL-F*
3 Prepare for search: add /search to input line.
4
5+*CTRL-K*
6+ Prepare for jumping to a buffer: add /buffer to input line.
7+
8 *CTRL-U*, *PgUp*
9 Go up in the timeline.
10
11@@ -206,8 +209,10 @@ _name_ is matched case-insensitively. It can be one of the following:
12 *LIST* [pattern]
13 List public channels, optionally matching the specified pattern.
14
15-*BUFFER* <name>
16- Switch to the buffer containing _name_.
17+*BUFFER* <index|name>
18+ Switch to the buffer at the _index_ position, or containing _name_.
19+ The buffer list will be filtered according to the passed name; entering the
20+ command will select the first buffer in the list.
21
22 *WHOIS* <nickname>
23 Get information about someone who is connected.
+85,
-16
1@@ -234,7 +234,8 @@ type BufferList struct {
2 tlHeight int
3 textWidth int
4
5- showBufferNumbers bool
6+ filterBuffers bool
7+ filterBuffersQuery string // lowercased
8 }
9
10 // NewBufferList returns a new BufferList.
11@@ -298,8 +299,9 @@ func (bs *BufferList) To(i int) bool {
12 return false
13 }
14
15-func (bs *BufferList) ShowBufferNumbers(enabled bool) {
16- bs.showBufferNumbers = enabled
17+func (bs *BufferList) FilterBuffers(enable bool, query string) {
18+ bs.filterBuffers = enable
19+ bs.filterBuffersQuery = strings.ToLower(query)
20 }
21
22 func (bs *BufferList) Next() {
23@@ -684,16 +686,20 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in
24 *offset = 0
25 }
26 }
27+ off := bs.VerticalBufferOffset(0, *offset)
28+ if off < 0 {
29+ off = len(bs.list)
30+ }
31
32 width--
33 drawVerticalLine(vx, x0+width, y0, height)
34 clearArea(vx, x0, y0, width, height)
35
36 indexPadding := 1 + int(math.Ceil(math.Log10(float64(len(bs.list)))))
37- for i, b := range bs.list[*offset:] {
38- bi := *offset + i
39+ y := y0
40+ for i, b := range bs.list[off:] {
41+ bi := off + i
42 x := x0
43- y := y0 + i
44 var st vaxis.Style
45 if b.unread {
46 st.Attribute |= vaxis.AttrBold
47@@ -702,7 +708,18 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in
48 if bi == bs.current || bi == bs.clicked {
49 st.Attribute |= vaxis.AttrReverse
50 }
51- if bs.showBufferNumbers {
52+
53+ var title string
54+ if b.title == "" {
55+ title = b.netName
56+ } else {
57+ title = b.title
58+ }
59+
60+ if bs.filterBuffers {
61+ if !strings.Contains(strings.ToLower(title), bs.filterBuffersQuery) {
62+ continue
63+ }
64 indexSt := st
65 indexSt.Foreground = ColorGray
66 indexText := fmt.Sprintf("%d:", bi+1)
67@@ -710,10 +727,7 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in
68 x = x0 + indexPadding
69 }
70
71- var title string
72- if b.title == "" {
73- title = b.netName
74- } else {
75+ if b.title != "" {
76 if bi == bs.current || bi == bs.clicked {
77 st := vaxis.Style{
78 Attribute: vaxis.AttrReverse,
79@@ -722,7 +736,6 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in
80 setCell(vx, x+1, y, ' ', st)
81 }
82 x += 2
83- title = b.title
84 }
85 title = truncate(vx, title, width-(x-x0), "\u2026")
86 printString(vx, &x, y, Styled(title, st))
87@@ -746,11 +759,28 @@ func (bs *BufferList) DrawVerticalBufferList(vx *Vaxis, x0, y0, width, height in
88 x = x0 + width - len(highlightText)
89 printString(vx, &x, y, Styled(highlightText, highlightSt))
90 }
91+
92+ y++
93 }
94 }
95
96 func (bs *BufferList) HorizontalBufferOffset(x int, offset int) int {
97- for i, b := range bs.list[offset:] {
98+ if bs.filterBuffers {
99+ offset = 0
100+ }
101+ i := 0
102+ for bi, b := range bs.list[offset:] {
103+ if bs.filterBuffers {
104+ var title string
105+ if b.title == "" {
106+ title = b.netName
107+ } else {
108+ title = b.title
109+ }
110+ if !strings.Contains(strings.ToLower(title), bs.filterBuffersQuery) {
111+ continue
112+ }
113+ }
114 if i > 0 {
115 x--
116 if x < 0 {
117@@ -759,8 +789,35 @@ func (bs *BufferList) HorizontalBufferOffset(x int, offset int) int {
118 }
119 x -= bs.bufferWidth(&b)
120 if x < 0 {
121- return offset + i
122+ return offset + bi
123+ }
124+ i++
125+ }
126+ return -1
127+}
128+
129+func (bs *BufferList) VerticalBufferOffset(y int, offset int) int {
130+ if !bs.filterBuffers {
131+ return offset + y
132+ }
133+
134+ for i, b := range bs.list {
135+ var title string
136+ if b.title == "" {
137+ title = b.netName
138+ } else {
139+ title = b.title
140 }
141+
142+ if bs.filterBuffers {
143+ if !strings.Contains(strings.ToLower(title), bs.filterBuffersQuery) {
144+ continue
145+ }
146+ }
147+ if y == 0 {
148+ return i
149+ }
150+ y--
151 }
152 return -1
153 }
154@@ -814,8 +871,13 @@ func (bs *BufferList) DrawHorizontalBufferList(vx *Vaxis, x0, y0, width int, off
155 }
156 x = x0
157
158- for i, b := range bs.list[*offset:] {
159- i := i + *offset
160+ off := bs.HorizontalBufferOffset(0, *offset)
161+ if off < 0 {
162+ off = len(bs.list)
163+ }
164+
165+ for i, b := range bs.list[off:] {
166+ i := i + off
167 if width <= x-x0 {
168 break
169 }
170@@ -837,6 +899,13 @@ func (bs *BufferList) DrawHorizontalBufferList(vx *Vaxis, x0, y0, width int, off
171 } else {
172 title = b.title
173 }
174+
175+ if bs.filterBuffers {
176+ if !strings.Contains(strings.ToLower(title), bs.filterBuffersQuery) {
177+ continue
178+ }
179+ }
180+
181 title = truncate(vx, title, width-x, "\u2026")
182 printString(vx, &x, y0, Styled(title, st))
183
M
ui/ui.go
+12,
-6
1@@ -174,7 +174,7 @@ func (ui *UI) ClickedBuffer() int {
2 }
3
4 func (ui *UI) ClickBuffer(i int) {
5- if i < len(ui.bs.list) {
6+ if i >= 0 && i < len(ui.bs.list) {
7 ui.bs.clicked = i
8 }
9 }
10@@ -221,8 +221,8 @@ func (ui *UI) GoToBufferNo(i int) {
11 }
12 }
13
14-func (ui *UI) ShowBufferNumbers(enable bool) {
15- ui.bs.ShowBufferNumbers(enable)
16+func (ui *UI) FilterBuffers(enable bool, query string) {
17+ ui.bs.FilterBuffers(enable, query)
18 }
19
20 func (ui *UI) ClickedMember() int {
21@@ -284,8 +284,8 @@ func (ui *UI) HorizontalBufferOffset(x int) int {
22 return ui.bs.HorizontalBufferOffset(x, ui.channelOffset)
23 }
24
25-func (ui *UI) ChannelOffset() int {
26- return ui.channelOffset
27+func (ui *UI) VerticalBufferOffset(y int) int {
28+ return ui.bs.VerticalBufferOffset(y, ui.channelOffset)
29 }
30
31 func (ui *UI) MemberOffset() int {
32@@ -393,7 +393,13 @@ func (ui *UI) AddLines(netID, buffer string, before, after []Line) {
33 func (ui *UI) JumpBuffer(sub string) bool {
34 subLower := strings.ToLower(sub)
35 for i, b := range ui.bs.list {
36- if strings.Contains(strings.ToLower(b.title), subLower) {
37+ var title string
38+ if b.title == "" {
39+ title = b.netName
40+ } else {
41+ title = b.title
42+ }
43+ if strings.Contains(strings.ToLower(title), subLower) {
44 if ui.bs.To(i) {
45 ui.memberOffset = 0
46 }
+23,
-10
1@@ -1,6 +1,7 @@
2 package senpai
3
4 import (
5+ "strconv"
6 "strings"
7 "time"
8
9@@ -79,17 +80,29 @@ func (app *App) setStatus() {
10 func (app *App) setBufferNumbers() {
11 input := app.win.InputContent()
12 if !isCommand(input) {
13- app.win.ShowBufferNumbers(false)
14+ app.win.FilterBuffers(false, "")
15 return
16 }
17- commandEnd := len(input)
18- for i := 1; i < len(input); i++ {
19- if input[i] == ' ' {
20- commandEnd = i
21- break
22- }
23+ cmd, arg, _ := strings.Cut(string(input[1:]), " ")
24+ if cmd == "" || !strings.HasPrefix("buffer", cmd) {
25+ app.win.FilterBuffers(false, "")
26+ return
27+ }
28+ if _, err := strconv.Atoi(arg); err == nil {
29+ // Do not filter buffers if we are passing a buffer index
30+ arg = ""
31+ }
32+ app.win.FilterBuffers(true, arg)
33+}
34+
35+func (app *App) clearBufferCommand() {
36+ input := app.win.InputContent()
37+ if !isCommand(input) {
38+ return
39+ }
40+ cmd, _, _ := strings.Cut(string(input[1:]), " ")
41+ if cmd == "" || !strings.HasPrefix("buffer", cmd) {
42+ return
43 }
44- command := string(input[1:commandEnd])
45- showBufferNumbers := len(command) != 0 && strings.HasPrefix("buffer", command)
46- app.win.ShowBufferNumbers(showBufferNumbers)
47+ app.win.InputClear()
48 }