line editing: add an option to emit ESC [ 6 n and use results
This makes line editing able to recognize case when cursor was not at the beginning of the line. It may also be adapted later to find out display size (serial line users would love it). Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
071ede1e5d
commit
020f40693a
13
editors/vi.c
13
editors/vi.c
@ -151,7 +151,6 @@ struct globals {
|
||||
char erase_char; // the users erase character
|
||||
char last_input_char; // last char read from user
|
||||
|
||||
smalluint chars_to_parse;
|
||||
#if ENABLE_FEATURE_VI_DOT_CMD
|
||||
smallint adding2q; // are we currently adding user input to q
|
||||
int lmc_len; // length of last_modifying_cmd
|
||||
@ -235,7 +234,6 @@ struct globals {
|
||||
#define last_forward_char (G.last_forward_char )
|
||||
#define erase_char (G.erase_char )
|
||||
#define last_input_char (G.last_input_char )
|
||||
#define chars_to_parse (G.chars_to_parse )
|
||||
#if ENABLE_FEATURE_VI_READONLY
|
||||
#define readonly_mode (G.readonly_mode )
|
||||
#else
|
||||
@ -620,7 +618,7 @@ static void edit_file(char *fn)
|
||||
// poll to see if there is input already waiting. if we are
|
||||
// not able to display output fast enough to keep up, skip
|
||||
// the display update until we catch up with input.
|
||||
if (!chars_to_parse && mysleep(0) == 0) {
|
||||
if (!readbuffer[0] && mysleep(0) == 0) {
|
||||
// no input pending - so update output
|
||||
refresh(FALSE);
|
||||
show_status_line();
|
||||
@ -2206,7 +2204,7 @@ static int readit(void) // read (maybe cursor) key from stdin
|
||||
int c;
|
||||
|
||||
fflush(stdout);
|
||||
c = read_key(STDIN_FILENO, &chars_to_parse, readbuffer);
|
||||
c = read_key(STDIN_FILENO, readbuffer);
|
||||
if (c == -1) { // EOF/error
|
||||
go_bottom_and_clear_to_eol();
|
||||
cookmode(); // terminal to "cooked"
|
||||
@ -3851,10 +3849,11 @@ static void crash_dummy()
|
||||
cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
|
||||
|
||||
// is there already a command running?
|
||||
if (chars_to_parse > 0)
|
||||
if (readbuffer[0] > 0)
|
||||
goto cd1;
|
||||
cd0:
|
||||
startrbi = rbi = 0;
|
||||
readbuffer[0] = 'X';
|
||||
startrbi = rbi = 1;
|
||||
sleeptime = 0; // how long to pause between commands
|
||||
memset(readbuffer, '\0', sizeof(readbuffer));
|
||||
// generate a command by percentages
|
||||
@ -3928,7 +3927,7 @@ static void crash_dummy()
|
||||
}
|
||||
strcat(readbuffer, "\033");
|
||||
}
|
||||
chars_to_parse = strlen(readbuffer);
|
||||
readbuffer[0] = strlen(readbuffer + 1);
|
||||
cd1:
|
||||
totalcmds++;
|
||||
if (sleeptime > 0)
|
||||
|
@ -962,16 +962,23 @@ enum {
|
||||
KEYCODE_FUN11 = -22,
|
||||
KEYCODE_FUN12 = -23,
|
||||
#endif
|
||||
/* How long the longest ESC sequence we know? */
|
||||
KEYCODE_BUFFER_SIZE = 4
|
||||
KEYCODE_CURSOR_POS = -0x100,
|
||||
/* How long is the longest ESC sequence we know?
|
||||
* We want it big enough to be able to contain
|
||||
* cursor position sequence "ESC [ 9999 ; 9999 R"
|
||||
*/
|
||||
KEYCODE_BUFFER_SIZE = 16
|
||||
};
|
||||
/* Note: fd may be in blocking or non-blocking mode, both make sense.
|
||||
* For one, less uses non-blocking mode.
|
||||
* Only the first read syscall inside read_key may block indefinitely
|
||||
* (unless fd is in non-blocking mode),
|
||||
* subsequent reads will time out after a few milliseconds.
|
||||
* Return of -1 means EOF or error (errno == 0 on EOF).
|
||||
* buffer[0] is used as a counter of buffered chars and must be 0
|
||||
* on first call.
|
||||
*/
|
||||
int read_key(int fd, smalluint *nbuffered, char *buffer) FAST_FUNC;
|
||||
int64_t read_key(int fd, char *buffer) FAST_FUNC;
|
||||
|
||||
|
||||
/* Networking */
|
||||
|
@ -102,6 +102,18 @@ config FEATURE_EDITING_FANCY_PROMPT
|
||||
Setting this option allows for prompts to use things like \w and
|
||||
\$ and escape codes.
|
||||
|
||||
config FEATURE_EDITING_ASK_TERMINAL
|
||||
bool "Query cursor position from terminal"
|
||||
default n
|
||||
depends on FEATURE_EDITING
|
||||
help
|
||||
Allow usage of "ESC [ 6 n" sequence. Terminal answers back with
|
||||
current cursor position. This information is used to make line
|
||||
editing more robust in some cases.
|
||||
If you are not sure whether your terminals respond to this code
|
||||
correctly, or want to save on code size (about 300 bytes),
|
||||
then do not turn this option on.
|
||||
|
||||
config FEATURE_VERBOSE_CP_MESSAGE
|
||||
bool "Give more precise messages when copy fails (cp, mv etc)"
|
||||
default n
|
||||
|
@ -86,8 +86,8 @@ struct lineedit_statics {
|
||||
volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
|
||||
sighandler_t previous_SIGWINCH_handler;
|
||||
|
||||
unsigned cmdedit_x; /* real x terminal position */
|
||||
unsigned cmdedit_y; /* pseudoreal y terminal position */
|
||||
unsigned cmdedit_x; /* real x (col) terminal position */
|
||||
unsigned cmdedit_y; /* pseudoreal y (row) terminal position */
|
||||
unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */
|
||||
|
||||
unsigned cursor;
|
||||
@ -299,6 +299,16 @@ static void input_backward(unsigned num)
|
||||
|
||||
static void put_prompt(void)
|
||||
{
|
||||
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
|
||||
/* Ask terminal where is cursor now.
|
||||
* lineedit_read_key handles response and corrects
|
||||
* our idea of current cursor position.
|
||||
* Testcase: run "echo -n long_line_long_line_long_line",
|
||||
* then type in a long, wrapping command and try to
|
||||
* delete it using backspace key.
|
||||
*/
|
||||
out1str("\033" "[6n");
|
||||
#endif
|
||||
out1str(cmdedit_prompt);
|
||||
cursor = 0;
|
||||
{
|
||||
@ -1430,18 +1440,33 @@ static void win_changed(int nsig)
|
||||
signal(SIGWINCH, win_changed); /* rearm ourself */
|
||||
}
|
||||
|
||||
static int lineedit_read_key(smalluint *read_key_bufsize, char *read_key_buffer)
|
||||
static int lineedit_read_key(char *read_key_buffer)
|
||||
{
|
||||
int ic;
|
||||
int64_t ic;
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = STDIN_FILENO;
|
||||
pfd.events = POLLIN;
|
||||
do {
|
||||
poll_again:
|
||||
/* Wait for input. Can't just call read_key, it will return
|
||||
* at once if stdin is in non-blocking mode. */
|
||||
safe_poll(&pfd, 1, -1);
|
||||
/* note: read_key sets errno to 0 on success: */
|
||||
ic = read_key(STDIN_FILENO, read_key_bufsize, read_key_buffer);
|
||||
ic = read_key(STDIN_FILENO, read_key_buffer);
|
||||
if (ENABLE_FEATURE_EDITING_ASK_TERMINAL
|
||||
&& (int32_t)ic == KEYCODE_CURSOR_POS
|
||||
) {
|
||||
int col = ((ic >> 32) & 0x7fff) - 1;
|
||||
if (col > 0) {
|
||||
cmdedit_x += col;
|
||||
while (cmdedit_x >= cmdedit_termw) {
|
||||
cmdedit_x -= cmdedit_termw;
|
||||
cmdedit_y++;
|
||||
}
|
||||
}
|
||||
goto poll_again;
|
||||
}
|
||||
} while (errno == EAGAIN);
|
||||
return ic;
|
||||
}
|
||||
@ -1482,7 +1507,6 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
|
||||
#endif
|
||||
struct termios initial_settings;
|
||||
struct termios new_settings;
|
||||
smalluint read_key_bufsize;
|
||||
char read_key_buffer[KEYCODE_BUFFER_SIZE];
|
||||
|
||||
INIT_S();
|
||||
@ -1561,7 +1585,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
|
||||
|
||||
while (1) {
|
||||
fflush(NULL);
|
||||
ic = lineedit_read_key(&read_key_bufsize, read_key_buffer);
|
||||
ic = lineedit_read_key(read_key_buffer);
|
||||
|
||||
#if ENABLE_FEATURE_EDITING_VI
|
||||
newdelflag = 1;
|
||||
@ -1738,7 +1762,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
|
||||
sc = cursor;
|
||||
prev_ic = ic;
|
||||
|
||||
ic = lineedit_read_key(&read_key_bufsize, read_key_buffer);
|
||||
ic = lineedit_read_key(read_key_buffer);
|
||||
if (errno) /* error */
|
||||
goto prepare_to_die;
|
||||
|
||||
@ -1801,7 +1825,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
|
||||
put();
|
||||
break;
|
||||
case 'r'|VI_CMDMODE_BIT:
|
||||
ic = lineedit_read_key(&read_key_bufsize, read_key_buffer);
|
||||
ic = lineedit_read_key(read_key_buffer);
|
||||
if (errno) /* error */
|
||||
goto prepare_to_die;
|
||||
if (ic < ' ' || ic > 255) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
#include "libbb.h"
|
||||
|
||||
int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer)
|
||||
int64_t FAST_FUNC read_key(int fd, char *buffer)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
const char *seq;
|
||||
@ -67,9 +67,7 @@ int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer)
|
||||
};
|
||||
|
||||
errno = 0;
|
||||
n = 0;
|
||||
if (nbuffered)
|
||||
n = *nbuffered;
|
||||
n = (unsigned char) *buffer++;
|
||||
if (n == 0) {
|
||||
/* If no data, block waiting for input. If we read more
|
||||
* than the minimal ESC sequence size, the "n=0" below
|
||||
@ -148,11 +146,54 @@ int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer)
|
||||
}
|
||||
}
|
||||
/* We did not find matching sequence, it was a bare ESC.
|
||||
* We possibly read and stored more input in buffer[]
|
||||
* by now. */
|
||||
* We possibly read and stored more input in buffer[] by now. */
|
||||
|
||||
/* Try to decipher "ESC [ NNN ; NNN R" sequence */
|
||||
if (ENABLE_FEATURE_EDITING_ASK_TERMINAL
|
||||
&& n != 0
|
||||
&& buffer[0] == '['
|
||||
) {
|
||||
char *end;
|
||||
unsigned long row, col;
|
||||
|
||||
while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for cnt */
|
||||
if (safe_poll(&pfd, 1, 50) == 0) {
|
||||
/* No more data! */
|
||||
break;
|
||||
}
|
||||
errno = 0;
|
||||
if (safe_read(fd, buffer + n, 1) <= 0) {
|
||||
/* If EAGAIN, then fd is O_NONBLOCK and poll lied:
|
||||
* in fact, there is no data. */
|
||||
if (errno != EAGAIN)
|
||||
c = -1; /* otherwise it's EOF/error */
|
||||
goto ret;
|
||||
}
|
||||
if (buffer[n++] == 'R')
|
||||
goto got_R;
|
||||
}
|
||||
goto ret;
|
||||
got_R:
|
||||
if (!isdigit(buffer[1]))
|
||||
goto ret;
|
||||
row = strtoul(buffer + 1, &end, 10);
|
||||
if (*end != ';' || !isdigit(end[1]))
|
||||
goto ret;
|
||||
col = strtoul(end + 1, &end, 10);
|
||||
if (*end != 'R')
|
||||
goto ret;
|
||||
if (row < 1 || col < 1 || (row | col) > 0x7fff)
|
||||
goto ret;
|
||||
|
||||
buffer[-1] = 0;
|
||||
|
||||
/* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */
|
||||
c = (((-1 << 15) | row) << 16) | col;
|
||||
/* Return it in high-order word */
|
||||
return ((int64_t) c << 32) | (uint32_t)KEYCODE_CURSOR_POS;
|
||||
}
|
||||
|
||||
ret:
|
||||
if (nbuffered)
|
||||
*nbuffered = n;
|
||||
buffer[-1] = n;
|
||||
return c;
|
||||
}
|
||||
|
@ -96,7 +96,6 @@ struct globals {
|
||||
smallint pattern_valid;
|
||||
#endif
|
||||
smallint terminated;
|
||||
smalluint kbd_input_size;
|
||||
struct termios term_orig, term_less;
|
||||
char kbd_input[KEYCODE_BUFFER_SIZE];
|
||||
};
|
||||
@ -135,7 +134,6 @@ struct globals {
|
||||
#define terminated (G.terminated )
|
||||
#define term_orig (G.term_orig )
|
||||
#define term_less (G.term_less )
|
||||
#define kbd_input_size (G.kbd_input_size )
|
||||
#define kbd_input (G.kbd_input )
|
||||
#define INIT_G() do { \
|
||||
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
|
||||
@ -806,7 +804,7 @@ static void reinitialize(void)
|
||||
buffer_fill_and_print();
|
||||
}
|
||||
|
||||
static ssize_t getch_nowait(void)
|
||||
static int getch_nowait(void)
|
||||
{
|
||||
int rd;
|
||||
struct pollfd pfd[2];
|
||||
@ -839,7 +837,7 @@ static ssize_t getch_nowait(void)
|
||||
move_cursor(max_displayed_line + 2, less_gets_pos + 1);
|
||||
fflush(stdout);
|
||||
|
||||
if (kbd_input_size == 0) {
|
||||
if (kbd_input[0] == 0) { /* if nothing is buffered */
|
||||
#if ENABLE_FEATURE_LESS_WINCH
|
||||
while (1) {
|
||||
int r;
|
||||
@ -856,7 +854,7 @@ static ssize_t getch_nowait(void)
|
||||
|
||||
/* We have kbd_fd in O_NONBLOCK mode, read inside read_key()
|
||||
* would not block even if there is no input available */
|
||||
rd = read_key(kbd_fd, &kbd_input_size, kbd_input);
|
||||
rd = read_key(kbd_fd, kbd_input);
|
||||
if (rd == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
/* No keyboard input available. Since poll() did return,
|
||||
@ -872,9 +870,9 @@ static ssize_t getch_nowait(void)
|
||||
return rd;
|
||||
}
|
||||
|
||||
/* Grab a character from input without requiring the return key. If the
|
||||
* character is ASCII \033, get more characters and assign certain sequences
|
||||
* special return codes. Note that this function works best with raw input. */
|
||||
/* Grab a character from input without requiring the return key.
|
||||
* May return KEYCODE_xxx values.
|
||||
* Note that this function works best with raw input. */
|
||||
static int less_getch(int pos)
|
||||
{
|
||||
int i;
|
||||
|
Loading…
Reference in New Issue
Block a user