Skip to content

Commit

Permalink
fix: special case modified f3 key and cursor pos report
Browse files Browse the repository at this point in the history
A modified F3 key press has the same sequence as cursor position report.
We need to handle that by reporting both messages and letting the
program determine which one to listen for. On most cases, programs don't
care about modified F3 key press since terminals tend to have different
sequences for it.
  • Loading branch information
aymanbagabas committed Aug 13, 2024
1 parent 0dd6210 commit c2c195c
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 15 deletions.
21 changes: 19 additions & 2 deletions key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io"
"math/rand"
"reflect"
"regexp"
"runtime"
"sort"
"strings"
Expand Down Expand Up @@ -63,12 +64,23 @@ type seqTest struct {
msgs []Msg
}

var f3CurPosRegexp = regexp.MustCompile(`\x1b\[1;(\d+)R`)

// buildBaseSeqTests returns sequence tests that are valid for the
// detectSequence() function.
func buildBaseSeqTests() []seqTest {
td := []seqTest{}
for seq, key := range sequences {
td = append(td, seqTest{[]byte(seq), []Msg{KeyPressMsg(key)}})
k := KeyPressMsg(key)
st := seqTest{seq: []byte(seq), msgs: []Msg{k}}

// XXX: This is a special case to handle F3 key sequence and cursor
// position report having the same sequence. See [parseCsi] for more
// information.
if f3CurPosRegexp.MatchString(seq) {
st.msgs = []Msg{k, CursorPositionMsg{Row: 1, Column: int(key.Mod) + 1}}
}
td = append(td, st)
}

// Additional special cases.
Expand Down Expand Up @@ -219,7 +231,12 @@ func TestParseSequence(t *testing.T) {
buf := tc.seq
for len(buf) > 0 {
width, msg := parseSequence(buf)
events = append(events, msg)
switch msg := msg.(type) {
case multiMsg:
events = append(events, msg...)
default:
events = append(events, msg)
}
buf = buf[width:]
}
if !reflect.DeepEqual(tc.msgs, events) {
Expand Down
29 changes: 16 additions & 13 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,23 +300,26 @@ func parseCsi(b []byte) (int, Msg) {
return i, BlurMsg{}
case 'R':
// Cursor position report OR modified F3
if paramsLen == 0 {
return i, KeyPressMsg{Sym: KeyF3}
} else if paramsLen != 2 {
break
if paramsLen == 2 {

Check failure on line 303 in parse.go

View workflow job for this annotation

GitHub Actions / lint-soft

Magic number: 2, in <condition> detected (gomnd)
m := CursorPositionMsg{Row: csi.Param(0), Column: csi.Param(1)}
if csi.Param(0) == 1 && csi.Param(1)-1 <= int(ModMeta|ModShift|ModAlt|ModCtrl) {
// XXX: We cannot differentiate between cursor position report and
// CSI 1 ; <mod> R (which is modified F3) when the cursor is at the
// row 1. In this case, we report both messages.
//
// For a non ambiguous cursor position report, use
// [ansi.RequestExtendedCursorPosition] (DECXCPR) instead.
return i, multiMsg{KeyPressMsg{Sym: KeyF3, Mod: KeyMod(csi.Param(1) - 1)}, m}
}

return i, m
}

// XXX: We cannot differentiate between cursor position report and
// CSI 1 ; <mod> R (which is modified F3) when the cursor is at the
// row 1. In this case, we report a modified F3 event since it's more
// likely to be the case than the cursor being at the first row.
//
// For a non ambiguous cursor position report, use
// [ansi.RequestExtendedCursorPosition] (DECXCPR) instead.
if csi.Param(0) != 1 {
return i, CursorPositionMsg{Row: csi.Param(0), Column: csi.Param(1)}
if paramsLen != 0 {
return i, UnknownMsg(b[:i])
}

// Unmodified key F3 (CSI R)
fallthrough
case 'a', 'b', 'c', 'd', 'A', 'B', 'C', 'D', 'E', 'F', 'H', 'P', 'Q', 'S', 'Z':
var k KeyPressMsg
Expand Down

0 comments on commit c2c195c

Please sign in to comment.