diff options
author | Ron Yorston <rmy@pobox.com> | 2017-11-03 14:16:08 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-11-03 14:16:08 +0000 |
commit | d6ce08aeb85b3698ddaa281016b70e16aeb9fb35 (patch) | |
tree | 02ad9bc0684859515fe891f3d6b0a1086e0db156 /libbb/lineedit.c | |
parent | ab450021a99ba66126cc6d668fb06ec3829a572b (diff) | |
parent | a5060b8364faa7c677c8950f1315c451403b0660 (diff) | |
download | busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.tar.gz busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.tar.bz2 busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'libbb/lineedit.c')
-rw-r--r-- | libbb/lineedit.c | 88 |
1 files changed, 64 insertions, 24 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index d85057e72..c0edb7399 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -37,11 +37,6 @@ | |||
37 | * | 37 | * |
38 | * Unicode in PS1 is not fully supported: prompt length calulation is wrong, | 38 | * Unicode in PS1 is not fully supported: prompt length calulation is wrong, |
39 | * resulting in line wrap problems with long (multi-line) input. | 39 | * resulting in line wrap problems with long (multi-line) input. |
40 | * | ||
41 | * Multi-line PS1 (e.g. PS1="\n[\w]\n$ ") has problems with history | ||
42 | * browsing: up/down arrows result in scrolling. | ||
43 | * It stems from simplistic "cmdedit_y = cmdedit_prmt_len / cmdedit_termw" | ||
44 | * calculation of how many lines the prompt takes. | ||
45 | */ | 40 | */ |
46 | #include "busybox.h" | 41 | #include "busybox.h" |
47 | #include "NUM_APPLETS.h" | 42 | #include "NUM_APPLETS.h" |
@@ -133,7 +128,7 @@ struct lineedit_statics { | |||
133 | 128 | ||
134 | unsigned cmdedit_x; /* real x (col) terminal position */ | 129 | unsigned cmdedit_x; /* real x (col) terminal position */ |
135 | unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ | 130 | unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ |
136 | unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ | 131 | unsigned cmdedit_prmt_len; /* on-screen length of last/sole prompt line */ |
137 | 132 | ||
138 | unsigned cursor; | 133 | unsigned cursor; |
139 | int command_len; /* must be signed */ | 134 | int command_len; /* must be signed */ |
@@ -143,6 +138,7 @@ struct lineedit_statics { | |||
143 | CHAR_T *command_ps; | 138 | CHAR_T *command_ps; |
144 | 139 | ||
145 | const char *cmdedit_prompt; | 140 | const char *cmdedit_prompt; |
141 | const char *prompt_last_line; /* last/sole prompt line */ | ||
146 | 142 | ||
147 | #if ENABLE_USERNAME_OR_HOMEDIR | 143 | #if ENABLE_USERNAME_OR_HOMEDIR |
148 | char *user_buf; | 144 | char *user_buf; |
@@ -185,6 +181,7 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics; | |||
185 | #define command_len (S.command_len ) | 181 | #define command_len (S.command_len ) |
186 | #define command_ps (S.command_ps ) | 182 | #define command_ps (S.command_ps ) |
187 | #define cmdedit_prompt (S.cmdedit_prompt ) | 183 | #define cmdedit_prompt (S.cmdedit_prompt ) |
184 | #define prompt_last_line (S.prompt_last_line) | ||
188 | #define user_buf (S.user_buf ) | 185 | #define user_buf (S.user_buf ) |
189 | #define home_pwd_buf (S.home_pwd_buf ) | 186 | #define home_pwd_buf (S.home_pwd_buf ) |
190 | #define matches (S.matches ) | 187 | #define matches (S.matches ) |
@@ -473,14 +470,20 @@ static void beep(void) | |||
473 | bb_putchar('\007'); | 470 | bb_putchar('\007'); |
474 | } | 471 | } |
475 | 472 | ||
476 | static void put_prompt(void) | 473 | /* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor. |
474 | * cmdedit_y is always calculated for the last/sole prompt line. | ||
475 | */ | ||
476 | static void put_prompt_custom(bool is_full) | ||
477 | { | 477 | { |
478 | fputs(cmdedit_prompt, stdout); | 478 | fputs((is_full ? cmdedit_prompt : prompt_last_line), stdout); |
479 | cursor = 0; | 479 | cursor = 0; |
480 | cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ | 480 | cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ |
481 | cmdedit_x = cmdedit_prmt_len % cmdedit_termw; | 481 | cmdedit_x = cmdedit_prmt_len % cmdedit_termw; |
482 | } | 482 | } |
483 | 483 | ||
484 | #define put_prompt_last_line() put_prompt_custom(0) | ||
485 | #define put_prompt() put_prompt_custom(1) | ||
486 | |||
484 | /* Move back one character */ | 487 | /* Move back one character */ |
485 | /* (optimized for slow terminals) */ | 488 | /* (optimized for slow terminals) */ |
486 | static void input_backward(unsigned num) | 489 | static void input_backward(unsigned num) |
@@ -547,7 +550,7 @@ static void input_backward(unsigned num) | |||
547 | printf("\r" ESC"[%uA", cmdedit_y); | 550 | printf("\r" ESC"[%uA", cmdedit_y); |
548 | cmdedit_y = 0; | 551 | cmdedit_y = 0; |
549 | sv_cursor = cursor; | 552 | sv_cursor = cursor; |
550 | put_prompt(); /* sets cursor to 0 */ | 553 | put_prompt_last_line(); /* sets cursor to 0 */ |
551 | while (cursor < sv_cursor) | 554 | while (cursor < sv_cursor) |
552 | put_cur_glyph_and_inc_cursor(); | 555 | put_cur_glyph_and_inc_cursor(); |
553 | } else { | 556 | } else { |
@@ -568,18 +571,27 @@ static void input_backward(unsigned num) | |||
568 | } | 571 | } |
569 | } | 572 | } |
570 | 573 | ||
571 | /* draw prompt, editor line, and clear tail */ | 574 | /* See redraw and draw_full below */ |
572 | static void redraw(int y, int back_cursor) | 575 | static void draw_custom(int y, int back_cursor, bool is_full) |
573 | { | 576 | { |
574 | if (y > 0) /* up y lines */ | 577 | if (y > 0) /* up y lines */ |
575 | printf(ESC"[%uA", y); | 578 | printf(ESC"[%uA", y); |
576 | bb_putchar('\r'); | 579 | bb_putchar('\r'); |
577 | put_prompt(); | 580 | put_prompt_custom(is_full); |
578 | put_till_end_and_adv_cursor(); | 581 | put_till_end_and_adv_cursor(); |
579 | printf(SEQ_CLEAR_TILL_END_OF_SCREEN); | 582 | printf(SEQ_CLEAR_TILL_END_OF_SCREEN); |
580 | input_backward(back_cursor); | 583 | input_backward(back_cursor); |
581 | } | 584 | } |
582 | 585 | ||
586 | /* Move y lines up, draw last/sole prompt line, editor line[s], and clear tail. | ||
587 | * goal: redraw the prompt+input+cursor in-place, overwriting the previous */ | ||
588 | #define redraw(y, back_cursor) draw_custom((y), (back_cursor), 0) | ||
589 | |||
590 | /* Like above, but without moving up, and while using all the prompt lines. | ||
591 | * goal: draw a full prompt+input+cursor unrelated to a previous position. | ||
592 | * note: cmdedit_y always ends up relating to the last/sole prompt line */ | ||
593 | #define draw_full(back_cursor) draw_custom(0, (back_cursor), 1) | ||
594 | |||
583 | /* Delete the char in front of the cursor, optionally saving it | 595 | /* Delete the char in front of the cursor, optionally saving it |
584 | * for later putback */ | 596 | * for later putback */ |
585 | #if !ENABLE_FEATURE_EDITING_VI | 597 | #if !ENABLE_FEATURE_EDITING_VI |
@@ -1159,7 +1171,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1159 | int sav_cursor = cursor; | 1171 | int sav_cursor = cursor; |
1160 | goto_new_line(); | 1172 | goto_new_line(); |
1161 | showfiles(); | 1173 | showfiles(); |
1162 | redraw(0, command_len - sav_cursor); | 1174 | draw_full(command_len - sav_cursor); |
1163 | } | 1175 | } |
1164 | return; | 1176 | return; |
1165 | } | 1177 | } |
@@ -1835,14 +1847,37 @@ static void ask_terminal(void) | |||
1835 | #define ask_terminal() ((void)0) | 1847 | #define ask_terminal() ((void)0) |
1836 | #endif | 1848 | #endif |
1837 | 1849 | ||
1850 | /* Note about multi-line PS1 (e.g. "\n\w \u@\h\n> ") and prompt redrawing: | ||
1851 | * | ||
1852 | * If the prompt has any newlines, after we print it once we use only its last | ||
1853 | * line to redraw in-place, which makes it simpler to calculate how many lines | ||
1854 | * we should move the cursor up to align the redraw (cmdedit_y). The earlier | ||
1855 | * prompt lines just stay on screen and we redraw below them. | ||
1856 | * | ||
1857 | * Use cases for all prompt lines beyond the initial draw: | ||
1858 | * - After clear-screen (^L) or after displaying tab-completion choices, we | ||
1859 | * print the full prompt, as it isn't redrawn in-place. | ||
1860 | * - During terminal resize we could try to redraw all lines, but we don't, | ||
1861 | * because it requires delicate alignment, it's good enough with only the | ||
1862 | * last line, and doing it wrong is arguably worse than not doing it at all. | ||
1863 | * | ||
1864 | * Terminology wise, if it doesn't mention "full", then it means the last/sole | ||
1865 | * prompt line. We use the prompt (last/sole line) while redrawing in-place, | ||
1866 | * and the full where we need a fresh one unrelated to an earlier position. | ||
1867 | * | ||
1868 | * If PS1 is not multiline, the last/sole line and the full are the same string. | ||
1869 | */ | ||
1870 | |||
1838 | /* Called just once at read_line_input() init time */ | 1871 | /* Called just once at read_line_input() init time */ |
1839 | #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT | 1872 | #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT |
1840 | static void parse_and_put_prompt(const char *prmt_ptr) | 1873 | static void parse_and_put_prompt(const char *prmt_ptr) |
1841 | { | 1874 | { |
1842 | const char *p; | 1875 | const char *p; |
1843 | cmdedit_prompt = prmt_ptr; | 1876 | cmdedit_prompt = prompt_last_line = prmt_ptr; |
1844 | p = strrchr(prmt_ptr, '\n'); | 1877 | p = strrchr(prmt_ptr, '\n'); |
1845 | cmdedit_prmt_len = unicode_strwidth(p ? p+1 : prmt_ptr); | 1878 | if (p) |
1879 | prompt_last_line = p + 1; | ||
1880 | cmdedit_prmt_len = unicode_strwidth(prompt_last_line); | ||
1846 | put_prompt(); | 1881 | put_prompt(); |
1847 | } | 1882 | } |
1848 | #else | 1883 | #else |
@@ -2035,7 +2070,11 @@ static void parse_and_put_prompt(const char *prmt_ptr) | |||
2035 | if (cwd_buf != (char *)bb_msg_unknown) | 2070 | if (cwd_buf != (char *)bb_msg_unknown) |
2036 | free(cwd_buf); | 2071 | free(cwd_buf); |
2037 | # endif | 2072 | # endif |
2038 | cmdedit_prompt = prmt_mem_ptr; | 2073 | /* see comment (above this function) about multiline prompt redrawing */ |
2074 | cmdedit_prompt = prompt_last_line = prmt_mem_ptr; | ||
2075 | prmt_ptr = strrchr(cmdedit_prompt, '\n'); | ||
2076 | if (prmt_ptr) | ||
2077 | prompt_last_line = prmt_ptr + 1; | ||
2039 | put_prompt(); | 2078 | put_prompt(); |
2040 | } | 2079 | } |
2041 | #endif | 2080 | #endif |
@@ -2207,7 +2246,7 @@ static int32_t reverse_i_search(int timeout) | |||
2207 | match_buf[0] = '\0'; | 2246 | match_buf[0] = '\0'; |
2208 | 2247 | ||
2209 | /* Save and replace the prompt */ | 2248 | /* Save and replace the prompt */ |
2210 | saved_prompt = cmdedit_prompt; | 2249 | saved_prompt = prompt_last_line; |
2211 | saved_prmt_len = cmdedit_prmt_len; | 2250 | saved_prmt_len = cmdedit_prmt_len; |
2212 | goto set_prompt; | 2251 | goto set_prompt; |
2213 | 2252 | ||
@@ -2280,10 +2319,10 @@ static int32_t reverse_i_search(int timeout) | |||
2280 | cursor = match - matched_history_line; | 2319 | cursor = match - matched_history_line; |
2281 | //FIXME: cursor position for Unicode case | 2320 | //FIXME: cursor position for Unicode case |
2282 | 2321 | ||
2283 | free((char*)cmdedit_prompt); | 2322 | free((char*)prompt_last_line); |
2284 | set_prompt: | 2323 | set_prompt: |
2285 | cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf); | 2324 | prompt_last_line = xasprintf("(reverse-i-search)'%s': ", match_buf); |
2286 | cmdedit_prmt_len = unicode_strwidth(cmdedit_prompt); | 2325 | cmdedit_prmt_len = unicode_strwidth(prompt_last_line); |
2287 | goto do_redraw; | 2326 | goto do_redraw; |
2288 | } | 2327 | } |
2289 | } | 2328 | } |
@@ -2303,8 +2342,8 @@ static int32_t reverse_i_search(int timeout) | |||
2303 | if (matched_history_line) | 2342 | if (matched_history_line) |
2304 | command_len = load_string(matched_history_line); | 2343 | command_len = load_string(matched_history_line); |
2305 | 2344 | ||
2306 | free((char*)cmdedit_prompt); | 2345 | free((char*)prompt_last_line); |
2307 | cmdedit_prompt = saved_prompt; | 2346 | prompt_last_line = saved_prompt; |
2308 | cmdedit_prmt_len = saved_prmt_len; | 2347 | cmdedit_prmt_len = saved_prmt_len; |
2309 | redraw(cmdedit_y, command_len - cursor); | 2348 | redraw(cmdedit_y, command_len - cursor); |
2310 | 2349 | ||
@@ -2524,8 +2563,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2524 | case CTRL('L'): | 2563 | case CTRL('L'): |
2525 | vi_case(CTRL('L')|VI_CMDMODE_BIT:) | 2564 | vi_case(CTRL('L')|VI_CMDMODE_BIT:) |
2526 | /* Control-l -- clear screen */ | 2565 | /* Control-l -- clear screen */ |
2527 | printf(ESC"[H"); /* cursor to top,left */ | 2566 | /* cursor to top,left; clear to the end of screen */ |
2528 | redraw(0, command_len - cursor); | 2567 | printf(ESC"[H" ESC"[J"); |
2568 | draw_full(command_len - cursor); | ||
2529 | break; | 2569 | break; |
2530 | #if MAX_HISTORY > 0 | 2570 | #if MAX_HISTORY > 0 |
2531 | case CTRL('N'): | 2571 | case CTRL('N'): |