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

Initial version of newshell autocompletion #75

Merged
merged 4 commits into from Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
136 changes: 106 additions & 30 deletions librz/cons/dietline.c
Expand Up @@ -692,14 +692,114 @@ static void selection_widget_update(void) {
return;
}

static bool is_valid_buffer_limits(RzLineBuffer *buf, size_t start, size_t end, size_t s_len) {
if (start > end || s_len < end - start) {
return false;
}
if (start > buf->length || start + s_len >= RZ_LINE_BUFSIZE - 1) {
return false;
}
if (end > buf->length || end + s_len >= RZ_LINE_BUFSIZE - 1) {
return false;
}
return true;
}

static void replace_buffer_text(RzLineBuffer *buf, size_t start, size_t end, const char *s) {
size_t s_len = strlen (s);
if (!is_valid_buffer_limits (buf, start, end, s_len)) {
return;
}

size_t diff = end - start;
// FIXME: escape s
memmove (buf->data + start + s_len, buf->data + end, buf->length - end);
memmove (buf->data + start, s, s_len);
buf->length += s_len - diff;
buf->index += s_len - diff;
buf->data[buf->length] = '\0';
}

static char *get_max_common_pfx(RzPVector *options) {
const char *ref = rz_pvector_at (options, 0);
size_t min_common_len = strlen (ref);
void **it;
bool first = true;
rz_pvector_foreach (options, it) {
if (first) {
first = false;
continue;
}
char *s = *(char **)it;
size_t j;
for (j = 0; s[j] && ref[j] && s[j] == ref[j]; j++);
if (j < min_common_len) {
min_common_len = j;
}
}
return rz_str_ndup (ref, min_common_len);
}

static void print_options(int argc, const char **argv) {
int cols = (int)(rz_cons_get_size (NULL) * 0.82);
Copy link
Member

Choose a reason for hiding this comment

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

What's with 0.82 ? Subjectively pleasing fraction of screen to fill?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have no idea... I just copied this code from what was already there and extracted it in a function because I needed to do the same thing. It's probably a "random" number though, yeah.

size_t i, len;
const int sep = 3;
int slen, col = 10;

for (i = 0; i < argc && argv[i]; i++) {
int l = strlen (argv[i]);
if (sep + l > col) {
col = sep + l;
}
if (col > (cols >> 1)) {
col = (cols >> 1);
break;
}
}
for (len = i = 0; i < argc && argv[i]; i++) {
if (len + col > cols) {
rz_cons_printf ("\n");
len = 0;
}
rz_cons_printf ("%-*s ", col - sep, argv[i]);
slen = strlen (argv[i]);
len += (slen > col) ? (slen + sep) : (col + sep);
}
rz_cons_printf ("\n");
}

RZ_API void rz_line_autocomplete(void) {
char *p;
const char **argv = NULL;
int argc = 0, i, j, plen, len = 0;
int argc = 0, i, j, plen;
bool opt = false;
int cols = (int)(rz_cons_get_size (NULL) * 0.82);
RzCons *cons = rz_cons_singleton ();

if (I.ns_completion.run) {
RzLineNSCompletionResult *res = I.ns_completion.run (&I.buffer, I.prompt_type, I.ns_completion.run_user);
if (!res || rz_pvector_empty (&res->options)) {
// do nothing
} else if (rz_pvector_len (&res->options) == 1) {
// if there is just one option, just use it
bool is_at_end = I.buffer.length == I.buffer.index;
replace_buffer_text (&I.buffer, res->start, res->end, rz_pvector_at (&res->options, 0));
if (is_at_end && res->end_string) {
replace_buffer_text (&I.buffer, I.buffer.length, I.buffer.length, res->end_string);
}
} else {
// otherwise find maxcommonprefix, print it, and then print options
char *max_common_pfx = get_max_common_pfx (&res->options);
replace_buffer_text (&I.buffer, res->start, res->end, max_common_pfx);
free (max_common_pfx);

rz_cons_printf ("%s%s\n", I.prompt, I.buffer.data);
print_options (rz_pvector_len (&res->options), (const char **)rz_pvector_data (&res->options));
}

rz_line_ns_completion_result_free (res);
return;
}

/* prepare argc and argv */
if (I.completion.run) {
I.completion.opt = false;
Expand Down Expand Up @@ -801,35 +901,9 @@ RZ_API void rz_line_autocomplete(void) {

/* show options */
if (argc > 1 && I.echo) {
const int sep = 3;
int slen, col = 10;
#ifdef __WINDOWS__
rz_cons_win_printf (false, "%s%s\n", I.prompt, I.buffer.data);
#else
printf ("%s%s\n", I.prompt, I.buffer.data);
#endif
for (i = 0; i < argc && argv[i]; i++) {
int l = strlen (argv[i]);
if (sep + l > col) {
col = sep + l;
}
if (col > (cols >> 1)) {
col = (cols >> 1);
break;
}
}
for (len = i = 0; i < argc && argv[i]; i++) {
if (len + col > cols) {
printf ("\n");
len = 0;
}
printf ("%-*s ", col - sep, argv[i]);
slen = strlen (argv[i]);
len += (slen > col)? (slen + sep): (col + sep);
}
printf ("\n");
rz_cons_printf ("%s%s\n", I.prompt, I.buffer.data);
print_options (argc, argv);
}
fflush (stdout);
}

RZ_API const char *rz_line_readline(void) {
Expand Down Expand Up @@ -1803,6 +1877,7 @@ RZ_API const char *rz_line_readline_cb(RzLineReadCallback cb, void *user) {
}
} else {
rz_line_autocomplete ();
rz_cons_flush ();
}
break;
case 13: // enter
Expand Down Expand Up @@ -1880,6 +1955,7 @@ RZ_API const char *rz_line_readline_cb(RzLineReadCallback cb, void *user) {
if (I.sel_widget && I.buffer.length != prev_buflen) {
prev_buflen = I.buffer.length;
rz_line_autocomplete ();
rz_cons_flush ();
}
prev = buf[0];
if (I.echo) {
Expand Down
2 changes: 1 addition & 1 deletion librz/cons/input.c
Expand Up @@ -362,7 +362,7 @@ RZ_API int rz_cons_fgets(char *buf, int len, int argc, const char **argv) {
#endif
errno = 0;
if (cons->user_fgets) {
RETURN (cons->user_fgets (buf, len));
RETURN (cons->user_fgets (buf, len, cons->user_fgets_user));
}
printf ("%s", cons->line->prompt);
fflush (stdout);
Expand Down
45 changes: 45 additions & 0 deletions librz/cons/line.c
Expand Up @@ -6,6 +6,11 @@
static RzLine rz_line_instance;
#define I rz_line_instance

static void rz_line_nscompletion_init(RzLineNSCompletion *c) {
c->run = NULL;
c->run_user = NULL;
}

RZ_API RzLine *rz_line_singleton(void) {
return &rz_line_instance;
}
Expand All @@ -28,6 +33,7 @@ RZ_API RzLine *rz_line_new(void) {
eprintf ("error: rz_line_dietline_init\n");
}
rz_line_completion_init (&I.completion, 4096);
rz_line_nscompletion_init (&I.ns_completion);
return &I;
}

Expand Down Expand Up @@ -104,4 +110,43 @@ RZ_API void rz_line_completion_clear(RzLineCompletion *completion) {
rz_pvector_clear (&completion->args);
}

/**
* Create an empty completion result with no available options.
*
* \param start Value for \p RzLineNSCompletionResult.start
* \param end Value for \p RzLineNSCompletionResult.end
* \param end_string Text that should be inserted after the only option available is autocompleted. When NULL, it defaults to " " (without quotes)
*/
RZ_API RzLineNSCompletionResult *rz_line_ns_completion_result_new(size_t start, size_t end, const char *end_string) {
RzLineNSCompletionResult *res = RZ_NEW0 (RzLineNSCompletionResult);
if (!res) {
return NULL;
}
rz_pvector_init (&res->options, (RzPVectorFree)free);
res->start = start;
res->end = end;
if (!end_string) {
end_string = " ";
}
res->end_string = end_string;
return res;
}

/**
* Free a previously allocated RzLineNSCompletionResult
*/
RZ_API void rz_line_ns_completion_result_free(RzLineNSCompletionResult *res) {
if (res) {
rz_pvector_fini (&res->options);
free (res);
}
}

/**
* Add a new option to the list of possible autocomplete-able values.
*/
RZ_API void rz_line_ns_completion_result_add(RzLineNSCompletionResult *res, const char *option) {
rz_pvector_push (&res->options, strdup (option));
}

#include "dietline.c"
2 changes: 1 addition & 1 deletion librz/core/Makefile
Expand Up @@ -8,7 +8,7 @@ RZ_DEPS+=rz_reg rz_search rz_syscall rz_socket rz_magic rz_crypto

OBJS=core.o cmd.o cfile.o cconfig.o visual.o cio.o yank.o libs.o agraph.o
OBJS+=fortune.o hack.o vasm.o patch.o cbin.o rtr.o cmd_api.o cmd_descs.o
OBJS+=carg.o canal.o project.o gdiff.o casm.o disasm.o cplugin.o
OBJS+=carg.o canal.o cautocmpl.o project.o gdiff.o casm.o disasm.o cplugin.o
OBJS+=vmenus.o vmenus_graph.o vmenus_zigns.o zdiff.o citem.o
OBJS+=task.o panels.o pseudo.o vmarks.o anal_tp.o anal_objc.o blaze.o
OBJS+=cannotated_code.o serialize_core.o
Expand Down