Skip to content
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 6 commits into from Aug 5, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions libr/cons/cons.c
Expand Up @@ -511,7 +511,7 @@ R_API void r_cons_fill_line() {
R_API void r_cons_clear_line(int std_err) {
#if __WINDOWS__
if (I.ansicon) {
fprintf (std_err? stderr: stdout,"\x1b[0K\r");
fprintf (std_err? stderr: stdout,"%s", ANSI_ESC_CLEAR_LINE);
} else {
char white[1024];
memset (&white, ' ', sizeof (white));
Expand All @@ -525,7 +525,7 @@ R_API void r_cons_clear_line(int std_err) {
fprintf (std_err? stderr: stdout, "\r%s\r", white);
}
#else
fprintf (std_err? stderr: stdout,"\x1b[0K\r");
fprintf (std_err? stderr: stdout,"%s", ANSI_ESC_CLEAR_LINE);
#endif
fflush (std_err? stderr: stdout);
}
Expand Down
200 changes: 187 additions & 13 deletions libr/cons/dietline.c
Expand Up @@ -442,7 +442,112 @@ R_API int r_line_hist_chop(const char *file, int limit) {
return 0;
}

static void draw_selection_widget () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all these functions, according to the style, should have ( without a space before it.

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 ? "\x1b[40m" : Color_INVERT; // XXX colors from palette
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont hardcode ansi escapes. Use the Color_ macros

char *selected_color = cons->color ? "\x1b[41m" : Color_INVERT_RESET; // XXX colors from palette
bool scrollbar = sel_widget->options_len > R_SELWIDGET_MAXH;
int scrollbar_y = scrollbar
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be clearer to just do:

int scrollbar_y = 0, scrollbar_l = 0;
if (scrollbar) {
   scrollbar_y = ...;
   scrollbar_l = ...;
}

? (R_SELWIDGET_MAXH * (sel_widget->selection - sel_widget->scroll)) / sel_widget->options_len
: 0;
int scrollbar_l = scrollbar
? (R_SELWIDGET_MAXH * R_SELWIDGET_MAXH) / sel_widget->options_len
: 0;

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_printf ("%s", background_color);
r_cons_printf ("%s", R_BETWEEN(scrollbar_y, y, scrollbar_y + scrollbar_l) ? "█" : " ");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this character in define.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space before (

}
}

r_cons_gotoxy (pos_x + I.buffer.length, pos_y);
r_cons_printf ("%s", Color_RESET_BG);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use rcons.memcat, no need to format a %s

r_cons_flush();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space

}

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The 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) {
strncpy (I.buffer.data, sel_widget->options[sel_widget->selection], R_LINE_BUFSIZE - 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strncpy and strlen? Thats innefficient

I.buffer.length = strlen (I.buffer.data);
I.buffer.index = I.buffer.length;
selection_widget_erase ();
}
}

static void selection_widget_update () {
if (I.completion.argc == 0 || I.buffer.length == 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;
Expand All @@ -456,6 +561,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
Expand Down Expand Up @@ -530,6 +640,14 @@ R_API void r_line_autocomplete() {
}
}

if (I.offset_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;
Expand Down Expand Up @@ -575,6 +693,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';
Expand All @@ -597,7 +716,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);
Expand Down Expand Up @@ -632,7 +751,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 ();
Expand All @@ -644,7 +766,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--;
}
Expand Down Expand Up @@ -831,7 +956,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--;
}
Expand All @@ -840,7 +968,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 ();
Expand Down Expand Up @@ -874,10 +1005,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';
Expand Down Expand Up @@ -905,6 +1043,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) {
Expand All @@ -927,7 +1069,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);
Expand Down Expand Up @@ -956,6 +1098,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) {
fflush (stdout);
}

if (I.sel_widget) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to check for NULL in free-like functions

R_FREE (I.sel_widget);
}

// should be here or not?
if (!memcmp (I.buffer.data, "!history", 8)) {
r_line_hist_list ();
Expand All @@ -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';
Expand All @@ -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);
Expand Down Expand Up @@ -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--;
}
Expand All @@ -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 ();
Expand Down Expand 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--;
}
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -1557,6 +1727,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) {
fflush (stdout);
}

if (I.sel_widget) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to check for null before using free-like functions

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') {
Expand Down
1 change: 1 addition & 0 deletions libr/core/core.c
Expand Up @@ -2222,6 +2222,7 @@ R_API void r_core_bind_cons(RCore *core) {
core->cons->cb_break = (RConsBreakCallback)r_core_break;
core->cons->cb_sleep_begin = (RConsSleepBeginCallback)r_core_sleep_begin;
core->cons->cb_sleep_end = (RConsSleepEndCallback)r_core_sleep_end;
core->cons->cb_task_oneshot = (RConsQueueTaskOneshot) r_core_task_enqueue_oneshot;
core->cons->user = (void*)core;
}

Expand Down