From deccdb1ec5079d490bd390c14b3f9a25c8d574db Mon Sep 17 00:00:00 2001
From: Junegunn Choi <junegunn.c@gmail.com>
Date: Mon, 23 Jan 2017 12:47:55 +0900
Subject: [PATCH] Cursor postition response can be preceded by user key strokes

---
 src/tui/light.go | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/src/tui/light.go b/src/tui/light.go
index 7bc2ec21..cfccc4b5 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"regexp"
 	"strconv"
 	"strings"
 	"syscall"
@@ -20,10 +21,13 @@ const (
 	defaultHeight = 24
 
 	escPollInterval = 5
+	offsetPollTries = 10
 )
 
 const consoleDevice string = "/dev/tty"
 
+var offsetRegexp *regexp.Regexp = regexp.MustCompile("\x1b\\[([0-9]+);([0-9]+)R")
+
 func openTtyIn() *os.File {
 	in, err := os.OpenFile(consoleDevice, syscall.O_RDONLY, 0)
 	if err != nil {
@@ -133,18 +137,14 @@ func (r *LightRenderer) defaultTheme() *ColorTheme {
 func (r *LightRenderer) findOffset() (row int, col int) {
 	r.csi("6n")
 	r.flush()
-	bytes := r.getBytesInternal([]byte{})
-
-	// ^[[*;*R
-	if len(bytes) > 5 && bytes[0] == 27 && bytes[1] == 91 && bytes[len(bytes)-1] == 'R' {
-		nums := strings.Split(string(bytes[2:len(bytes)-1]), ";")
-		if len(nums) == 2 {
-			return atoi(nums[0], 0) - 1, atoi(nums[1], 0) - 1
+	bytes := []byte{}
+	for tries := 0; tries < offsetPollTries; tries++ {
+		bytes = r.getBytesInternal(bytes, tries > 0)
+		offsets := offsetRegexp.FindSubmatch(bytes)
+		if len(offsets) > 2 {
+			return atoi(string(offsets[1]), 0) - 1, atoi(string(offsets[2]), 0) - 1
 		}
-		return -1, -1
 	}
-
-	// No idea
 	return -1, -1
 }
 
@@ -274,18 +274,18 @@ func (r *LightRenderer) getch(nonblock bool) (int, bool) {
 }
 
 func (r *LightRenderer) getBytes() []byte {
-	return r.getBytesInternal(r.buffer)
+	return r.getBytesInternal(r.buffer, false)
 }
 
-func (r *LightRenderer) getBytesInternal(buffer []byte) []byte {
-	c, ok := r.getch(false)
-	if !ok {
+func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte {
+	c, ok := r.getch(nonblock)
+	if !nonblock && !ok {
 		r.Close()
 		errorExit("Failed to read " + consoleDevice)
 	}
 
 	retries := 0
-	if c == ESC {
+	if c == ESC || nonblock {
 		retries = r.escDelay / escPollInterval
 	}
 	buffer = append(buffer, byte(c))
-- 
GitLab