New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vim-like autocompletion in visual offset prompt #10912
Merged
Merged
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
7c89189
Feat: autocompletion widget for offset prompt in visual mode
cyanpencil 3d8d3a7
Fix: screen refresh when erasing sel_widget
cyanpencil 934e398
Refactor: many small changes to the autocompletion widget
cyanpencil ec2bcc5
Fix: colors in selection widget taken from palette
cyanpencil 6589ba7
Refactor: do not use utf8 inside selection widget
cyanpencil 8d8ee06
Refactor: rename ANSI_ESC_CLEAR_LINE to R_CONS_CLEAR_LINE
cyanpencil File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -442,7 +442,114 @@ R_API int r_line_hist_chop(const char *file, int limit) { | |
return 0; | ||
} | ||
|
||
static void draw_selection_widget() { | ||
RCons *cons = r_cons_singleton (); | ||
RSelWidget *sel_widget = I.sel_widget; | ||
int y, pos_y = cons->rows, pos_x = r_str_ansi_len (I.prompt); | ||
|
||
for (y = 0; y < sel_widget->options_len; y++) { | ||
sel_widget->w = R_MAX (sel_widget->w, strlen (sel_widget->options[y])); | ||
} | ||
sel_widget->w = R_MIN (sel_widget->w, R_SELWIDGET_MAXW); | ||
|
||
char *background_color = cons->color ? cons->pal.widget_bg : Color_INVERT; | ||
char *selected_color = cons->color ? cons->pal.widget_sel : Color_INVERT_RESET; | ||
bool scrollbar = sel_widget->options_len > R_SELWIDGET_MAXH; | ||
int scrollbar_y = 0, scrollbar_l = 0; | ||
if (scrollbar) { | ||
scrollbar_y = (R_SELWIDGET_MAXH * (sel_widget->selection - sel_widget->scroll)) / sel_widget->options_len; | ||
scrollbar_l = (R_SELWIDGET_MAXH * R_SELWIDGET_MAXH) / sel_widget->options_len; | ||
} | ||
|
||
for (y = 0; y < R_MIN (sel_widget->h, R_SELWIDGET_MAXH); y++) { | ||
r_cons_gotoxy (pos_x + 1, pos_y - y - 1); | ||
int scroll = R_MAX (0, sel_widget->selection - sel_widget->scroll); | ||
char *option = y < sel_widget->options_len ? sel_widget->options[y + scroll] : ""; | ||
r_cons_printf ("%s", sel_widget->selection == y + scroll ? selected_color : background_color); | ||
r_cons_printf ("%-*.*s", sel_widget->w, sel_widget->w, option); | ||
if (scrollbar) { | ||
r_cons_strcat (background_color); | ||
r_cons_printf ("%s", R_BETWEEN (scrollbar_y, y, scrollbar_y + scrollbar_l) ? SELWIDGET_SCROLLCHAR : " "); | ||
} else { | ||
r_cons_memcat (" ", 1); | ||
} | ||
} | ||
|
||
r_cons_gotoxy (pos_x + I.buffer.length, pos_y); | ||
r_cons_memcat (Color_RESET_BG, 5); | ||
r_cons_flush (); | ||
} | ||
|
||
static void selection_widget_up() { | ||
RSelWidget *sel_widget = I.sel_widget; | ||
if (sel_widget && sel_widget->selection < sel_widget->options_len - 1) { | ||
sel_widget->selection++; | ||
sel_widget->scroll = R_MIN (sel_widget->scroll + 1, R_SELWIDGET_MAXH - 1); | ||
} | ||
} | ||
|
||
static void selection_widget_down() { | ||
RSelWidget *sel_widget = I.sel_widget; | ||
if (sel_widget && sel_widget->selection > 0) { | ||
sel_widget->selection--; | ||
sel_widget->scroll = R_MAX (sel_widget->scroll - 1, 0); | ||
} | ||
} | ||
|
||
static void print_rline_task(void *core) { | ||
r_cons_clear_line (0); | ||
r_cons_printf ("%s%s%s", Color_RESET, I.prompt, I.buffer.data); | ||
r_cons_flush (); | ||
} | ||
|
||
static void selection_widget_erase() { | ||
RSelWidget *sel_widget = I.sel_widget; | ||
if (sel_widget) { | ||
sel_widget->options_len = 0; | ||
sel_widget->selection = -1; | ||
draw_selection_widget (); | ||
R_FREE(I.sel_widget); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing space |
||
RCons *cons = r_cons_singleton (); | ||
if (cons->event_resize && cons->event_data) { | ||
cons->event_resize (cons->event_data); | ||
cons->cb_task_oneshot (cons->user, print_rline_task, NULL); | ||
} | ||
} | ||
} | ||
|
||
static void selection_widget_select() { | ||
RSelWidget *sel_widget = I.sel_widget; | ||
if (sel_widget && sel_widget->selection < sel_widget->options_len) { | ||
I.buffer.length = R_MIN (strlen (sel_widget->options[sel_widget->selection]), R_LINE_BUFSIZE - 1); | ||
memcpy (I.buffer.data, sel_widget->options[sel_widget->selection], I.buffer.length); | ||
I.buffer.data[I.buffer.length] = '\0'; | ||
I.buffer.index = I.buffer.length; | ||
selection_widget_erase (); | ||
} | ||
} | ||
|
||
static void selection_widget_update() { | ||
if (I.completion.argc == 0 || | ||
(I.completion.argc == 1 && I.buffer.length >= strlen (I.completion.argv[0]))) { | ||
selection_widget_erase (); | ||
return; | ||
} | ||
if (!I.sel_widget) { | ||
RSelWidget *sel_widget = R_NEW0 (RSelWidget); | ||
I.sel_widget = sel_widget; | ||
} | ||
I.sel_widget->scroll = 0; | ||
I.sel_widget->selection = 0; | ||
I.sel_widget->options_len = I.completion.argc; | ||
I.sel_widget->options = I.completion.argv; | ||
I.sel_widget->h = R_MAX (I.sel_widget->h, I.completion.argc); | ||
draw_selection_widget (); | ||
r_cons_flush (); | ||
return; | ||
} | ||
|
||
R_API void r_line_autocomplete() { | ||
|
||
int argc = 0; | ||
char *p; | ||
const char **argv = NULL; | ||
|
@@ -456,6 +563,11 @@ R_API void r_line_autocomplete() { | |
argv = I.completion.argv; | ||
} | ||
|
||
if (I.sel_widget && !I.sel_widget->complete_common) { | ||
selection_widget_update (); | ||
return; | ||
} | ||
|
||
p = (char *) r_sub_str_lchr (I.buffer.data, 0, I.buffer.index, ' '); | ||
if (!p) { | ||
p = (char *) r_sub_str_lchr (I.buffer.data, 0, I.buffer.index, '@'); // HACK FOR r2 | ||
|
@@ -530,6 +642,14 @@ R_API void r_line_autocomplete() { | |
} | ||
} | ||
|
||
if (I.offset_prompt || I.file_prompt) { | ||
selection_widget_update (); | ||
if (I.sel_widget) { | ||
I.sel_widget->complete_common = false; | ||
} | ||
return; | ||
} | ||
|
||
/* show options */ | ||
if (opt > 1 && I.echo) { | ||
const int sep = 3; | ||
|
@@ -575,6 +695,7 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
char *tmp_ed_cmd, prev = 0; | ||
HANDLE hClipBoard; | ||
char *clipText; | ||
int prev_buflen = 0; | ||
|
||
I.buffer.index = I.buffer.length = 0; | ||
I.buffer.data[0] = '\0'; | ||
|
@@ -597,7 +718,7 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
|
||
if (I.echo) { | ||
r_cons_clear_line (0); | ||
printf ("\x1b[0K\r%s%s", I.prompt, I.buffer.data); | ||
printf ("%s%s%s", Color_RESET, I.prompt, I.buffer.data); | ||
fflush (stdout); | ||
} | ||
r_cons_break_push (NULL, NULL); | ||
|
@@ -632,7 +753,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
I.buffer.index = I.buffer.index? I.buffer.index - 1: 0; | ||
break; | ||
case 38: // up arrow | ||
if (gcomp) { | ||
if (I.sel_widget) { | ||
selection_widget_up (); | ||
draw_selection_widget (); | ||
} else if (gcomp) { | ||
gcomp_idx++; | ||
} else if (r_line_hist_up () == -1) { | ||
r_cons_break_pop (); | ||
|
@@ -644,7 +768,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
I.buffer.index + 1: I.buffer.length; | ||
break; | ||
case 40:// down arrow | ||
if (gcomp) { | ||
if (I.sel_widget) { | ||
selection_widget_up (); | ||
draw_selection_widget (); | ||
} else if (gcomp) { | ||
if (gcomp_idx > 0) { | ||
gcomp_idx--; | ||
} | ||
|
@@ -831,7 +958,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
paste (); | ||
break; | ||
case 14:// ^n | ||
if (gcomp) { | ||
if (I.sel_widget) { | ||
selection_widget_down (); | ||
draw_selection_widget (); | ||
} else if (gcomp) { | ||
if (gcomp_idx > 0) { | ||
gcomp_idx--; | ||
} | ||
|
@@ -840,7 +970,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
} | ||
break; | ||
case 16:// ^p | ||
if (gcomp) { | ||
if (I.sel_widget) { | ||
selection_widget_down (); | ||
draw_selection_widget (); | ||
} else if (gcomp) { | ||
gcomp_idx++; | ||
} else { | ||
r_line_hist_up (); | ||
|
@@ -874,10 +1007,17 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
break; | ||
/* tab */ | ||
case 9: // tab | ||
if (I.sel_widget) { | ||
I.sel_widget->complete_common = true; | ||
} | ||
r_line_autocomplete (); | ||
break; | ||
/* enter */ | ||
case 13: | ||
if (I.sel_widget) { | ||
selection_widget_select (); | ||
break; | ||
} | ||
if (gcomp && I.buffer.length > 0) { | ||
strncpy (I.buffer.data, gcomp_line, R_LINE_BUFSIZE - 1); | ||
I.buffer.data[R_LINE_BUFSIZE - 1] = '\0'; | ||
|
@@ -905,6 +1045,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
I.buffer.index++; | ||
break; | ||
} | ||
if (I.sel_widget && I.buffer.length != prev_buflen) { | ||
prev_buflen = I.buffer.length; | ||
r_line_autocomplete (); | ||
} | ||
prev = buf[0]; | ||
if (I.echo) { | ||
if (gcomp) { | ||
|
@@ -927,7 +1071,7 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
int chars = R_MAX (1, strlen (I.buffer.data)); // wtf? | ||
int len, cols = R_MAX (1, columns - r_str_ansi_len (I.prompt) - 2); | ||
/* print line */ | ||
printf ("\r%s", I.prompt); | ||
printf ("\r%s%s", Color_RESET, I.prompt); | ||
fwrite (I.buffer.data, 1, R_MIN (cols, chars), stdout); | ||
/* place cursor */ | ||
printf ("\r%s", I.prompt); | ||
|
@@ -956,6 +1100,8 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { | |
fflush (stdout); | ||
} | ||
|
||
R_FREE (I.sel_widget); | ||
|
||
// should be here or not? | ||
if (!memcmp (I.buffer.data, "!history", 8)) { | ||
r_line_hist_list (); | ||
|
@@ -980,6 +1126,7 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
#endif | ||
int ch, i = 0; /* grep completion */ | ||
char *tmp_ed_cmd, prev = 0; | ||
int prev_buflen = -1; | ||
|
||
I.buffer.index = I.buffer.length = 0; | ||
I.buffer.data[0] = '\0'; | ||
|
@@ -1002,7 +1149,7 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
|
||
if (I.echo) { | ||
r_cons_clear_line (0); | ||
printf ("\x1b[0K\r%s%s", I.prompt, I.buffer.data); | ||
printf ("%s%s%s", Color_RESET, I.prompt, I.buffer.data); | ||
fflush (stdout); | ||
} | ||
r_cons_break_push (NULL, NULL); | ||
|
@@ -1196,7 +1343,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
paste (); | ||
break; | ||
case 14:// ^n | ||
if (gcomp) { | ||
if (I.sel_widget) { | ||
selection_widget_down (); | ||
draw_selection_widget (); | ||
} else if (gcomp) { | ||
if (gcomp_idx > 0) { | ||
gcomp_idx--; | ||
} | ||
|
@@ -1205,7 +1355,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
} | ||
break; | ||
case 16:// ^p | ||
if (gcomp) { | ||
if (I.sel_widget) { | ||
selection_widget_up (); | ||
draw_selection_widget (); | ||
} else if (gcomp) { | ||
gcomp_idx++; | ||
} else { | ||
r_line_hist_up (); | ||
|
@@ -1278,15 +1431,21 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
break; | ||
/* arrows */ | ||
case 'A': // up arrow | ||
if (gcomp) { | ||
if (I.sel_widget) { | ||
selection_widget_up (); | ||
draw_selection_widget (); | ||
} else if (gcomp) { | ||
gcomp_idx++; | ||
} else if (r_line_hist_up () == -1) { | ||
r_cons_break_pop (); | ||
return NULL; | ||
} | ||
break; | ||
case 'B': // down arrow | ||
if (gcomp) { | ||
if (I.sel_widget) { | ||
selection_widget_down (); | ||
draw_selection_widget (); | ||
} else if (gcomp) { | ||
if (gcomp_idx > 0) { | ||
gcomp_idx--; | ||
} | ||
|
@@ -1450,9 +1609,16 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
I.buffer.length++; | ||
I.buffer.index++; | ||
} | ||
if (I.sel_widget) { | ||
I.sel_widget->complete_common = true; | ||
} | ||
r_line_autocomplete (); | ||
break; | ||
case 13: | ||
case 13: // enter | ||
if (I.sel_widget) { | ||
selection_widget_select (); | ||
break; | ||
} | ||
if (gcomp && I.buffer.length > 0) { | ||
strncpy (I.buffer.data, gcomp_line, R_LINE_BUFSIZE - 1); | ||
I.buffer.data[R_LINE_BUFSIZE - 1] = '\0'; | ||
|
@@ -1506,6 +1672,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
#endif | ||
break; | ||
} | ||
if (I.sel_widget && I.buffer.length != prev_buflen) { | ||
prev_buflen = I.buffer.length; | ||
r_line_autocomplete (); | ||
} | ||
prev = buf[0]; | ||
if (I.echo) { | ||
if (gcomp) { | ||
|
@@ -1528,7 +1698,7 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
int chars = R_MAX (1, strlen (I.buffer.data)); // wtf? | ||
int len, cols = R_MAX (1, columns - r_str_ansi_len (I.prompt) - 2); | ||
/* print line */ | ||
printf ("\r%s", I.prompt); | ||
printf ("\r%s%s", Color_RESET, I.prompt); | ||
fwrite (I.buffer.data, 1, R_MIN (cols, chars), stdout); | ||
/* place cursor */ | ||
printf ("\r%s", I.prompt); | ||
|
@@ -1557,6 +1727,8 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { | |
fflush (stdout); | ||
} | ||
|
||
R_FREE (I.sel_widget); | ||
|
||
// should be here or not? | ||
if (!memcmp (I.buffer.data, "!history", 8)) { | ||
// if (I.buffer.data[0]=='!' && I.buffer.data[1]=='\0') { | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SELWIDGET_SCROLLCHAR is utf8, you must set a var at the begining of the function to be that one or an ascii alternative when utf8 is not set