From 9d967ad29cb3ddb402f70d0cbadd812cbbca1564 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sat, 17 Oct 2020 15:41:31 +0000 Subject: [PATCH] Improve completion system - rename and extend CompletionEntry as CompletionDef - create xxx_completion CompletionDef structures - rename xxx_completion() functions to xxx_complete() - register CompletionDef structs with qe_register_completion() - add (start, end) range in CompleteState - use eb_puts() and eb_putc() instead of eb_printf() - simplify eb_delete_chars() - ignore left pane in compare-windows() - simplify qe_list_bindings() - improve command and variable completion popup --- bufed.c | 5 +- buffer.c | 11 +-- charset.c | 2 +- dired.c | 14 +-- extras.c | 82 +++++++++------- image.c | 10 +- input.c | 8 +- latex-mode.c | 8 +- qe.c | 264 ++++++++++++++++++++++++++++++++++++--------------- qe.h | 39 ++++---- qscript.c | 22 ++++- tty.c | 4 +- util.c | 2 +- variables.c | 61 +++++++++++- variables.h | 3 +- 15 files changed, 374 insertions(+), 161 deletions(-) diff --git a/bufed.c b/bufed.c index f40a0bf..7dfedd7 100644 --- a/bufed.c +++ b/bufed.c @@ -152,11 +152,10 @@ static void build_bufed_list(BufedState *bs, EditState *s) b->cur_style = BUFED_STYLE_DIRECTORY; else b->cur_style = BUFED_STYLE_FILENAME; - eb_printf(b, "%s", - make_user_path(path, sizeof(path), b1->filename)); + eb_puts(b, make_user_path(path, sizeof(path), b1->filename)); b->cur_style = style0; } - eb_printf(b, "\n"); + eb_putc(b, '\n'); } b->modified = 0; b->flags |= BF_READONLY; diff --git a/buffer.c b/buffer.c index 7b777bc..5057cbb 100644 --- a/buffer.c +++ b/buffer.c @@ -1531,16 +1531,7 @@ int eb_delete_uchar(EditBuffer *b, int offset) */ int eb_delete_chars(EditBuffer *b, int offset, int n) { - int offset1 = eb_skip_chars(b, offset, n); - int size = offset1 - offset; - - if (size < 0) { - offset += size; - offset1 -= size; - size = -size; - } - - return eb_delete(b, offset, size); + return eb_delete_range(b, offset, eb_skip_chars(b, offset, n)); } /* XXX: only stateless charsets are supported */ diff --git a/charset.c b/charset.c index bb286bb..35fc8fe 100644 --- a/charset.c +++ b/charset.c @@ -1130,7 +1130,7 @@ void qe_register_charset(struct QECharset *charset) *pp = charset; } -void charset_completion(CompleteState *cp) +void charset_complete(CompleteState *cp) { QECharset *charset; char name[32]; diff --git a/dired.c b/dired.c index 73ae355..f2a5574 100644 --- a/dired.c +++ b/dired.c @@ -683,11 +683,11 @@ static void dired_update_buffer(DiredState *ds, EditBuffer *b, EditState *s, if (DIRED_HEADER) { int seq = ' '; b->cur_style = DIRED_STYLE_HEADER; - eb_printf(b, " Directory of "); + eb_puts(b, " Directory of "); b->cur_style = DIRED_STYLE_DIRECTORY; - eb_printf(b, "%s", ds->path); + eb_puts(b, ds->path); b->cur_style = DIRED_STYLE_HEADER; - eb_printf(b, "\n "); + eb_puts(b, "\n "); if (ds->ndirs) { eb_printf(b, "%c %d %s", seq, ds->ndirs, inflect(ds->ndirs, "directory", "directories")); @@ -717,7 +717,7 @@ static void dired_update_buffer(DiredState *ds, EditBuffer *b, EditState *s, if (ds->ndirs + ds->ndirs_hidden + ds->nfiles + ds->nfiles_hidden == 0) { eb_printf(b, "%c empty", seq); } - eb_printf(b, "\n"); + eb_putc(b, '\n'); } b->cur_style = DIRED_STYLE_NORMAL; @@ -767,12 +767,12 @@ static void dired_update_buffer(DiredState *ds, EditBuffer *b, EditState *s, else b->cur_style = DIRED_STYLE_FILENAME; - eb_printf(b, "%s", dip->name); + eb_puts(b, dip->name); if (1) { int trailchar = get_trailchar(dip->mode); if (trailchar) { - eb_printf(b, "%c", trailchar); + eb_putc(b, trailchar); } } if (S_ISLNK(dip->mode) @@ -780,7 +780,7 @@ static void dired_update_buffer(DiredState *ds, EditBuffer *b, EditState *s, eb_printf(b, " -> %s", buf); } b->cur_style = DIRED_STYLE_NORMAL; - eb_printf(b, "\n"); + eb_putc(b, '\n'); } b->modified = 0; b->flags |= BF_READONLY; diff --git a/extras.c b/extras.c index 3b5a475..73d589e 100644 --- a/extras.c +++ b/extras.c @@ -123,21 +123,24 @@ void do_compare_windows(EditState *s, int argval) s1 = s; /* Should use same internal function as for next_window */ - if (s1->next_window) - s2 = s1->next_window; - else - s2 = qs->first_window; - + for (s2 = s1;;) { + s2 = s2->next_window; + if (s2 == NULL) + s2 = qs->first_window; + if (s2 == s1) { + /* single window: bail out */ + return; + } + if (s2->b->flags & BF_DIRED) + continue; + break; + } if (argval != NO_ARG) { if (argval & 4) qs->ignore_spaces ^= 1; if (argval & 16) qs->ignore_comments ^= 1; } - if (s1 == s2) { - /* single window: bail out */ - return; - } size1 = s1->b->total_size; size2 = s2->b->total_size; @@ -867,8 +870,7 @@ static void do_unset_key(EditState *s, const char *keystr, int local) /*---------------- help ----------------*/ -static int qe_list_bindings(char *buf, int size, CmdDef *d, - ModeDef *mode, int inherit) +int qe_list_bindings(CmdDef *d, ModeDef *mode, int inherit, char *buf, int size) { int pos; buf_t outbuf, *out; @@ -904,7 +906,7 @@ void do_show_bindings(EditState *s, const char *cmd_name) put_status(s, "No command %s", cmd_name); return; } - if (qe_list_bindings(buf, sizeof(buf), d, s->mode, 1)) { + if (qe_list_bindings(d, s->mode, 1, buf, sizeof(buf))) { put_status(s, "%s is bound to %s", cmd_name, buf); } else { put_status(s, "%s is not bound to any key", cmd_name); @@ -916,18 +918,21 @@ static void print_bindings(EditBuffer *b, ModeDef *mode) char buf[256]; CmdDef *d; int gfound; + //int start = b->total_size; + /* XXX: should sort matches */ gfound = 0; d = qe_state.first_cmd; while (d != NULL) { while (d->name != NULL) { - if (qe_list_bindings(buf, sizeof(buf), d, mode, 0)) { + if (qe_list_bindings(d, mode, 0, buf, sizeof(buf))) { if (!gfound) { if (mode) { eb_printf(b, "\n%s mode bindings:\n\n", mode->name); } else { eb_printf(b, "\nGlobal bindings:\n\n"); } + //start = b->total_size; gfound = 1; } eb_printf(b, "%24s : %s\n", d->name, buf); @@ -936,6 +941,7 @@ static void print_bindings(EditBuffer *b, ModeDef *mode) } d = d->action.next; } + //do_sort_span(b, start, b->total_size, 0, NO_ARG); } void do_describe_bindings(EditState *s) @@ -966,7 +972,10 @@ void do_apropos(EditState *s, const char *str) if (!b) return; - eb_printf(b, "\n"); + eb_putc(b, '\n'); + + /* XXX: should sort matches */ + /* XXX: should print description */ found = 0; d = qs->first_cmd; @@ -975,12 +984,12 @@ void do_apropos(EditState *s, const char *str) if (strstr(d->name, str)) { /* print name and prototype */ qe_get_prototype(d, buf, sizeof(buf)); - eb_printf(b, "command: %s(%s)", d->name, buf); - if (qe_list_bindings(buf, sizeof(buf), d, s->mode, 1)) + eb_printf(b, "command: %s%s", d->name, buf); + if (qe_list_bindings(d, s->mode, 1, buf, sizeof(buf))) eb_printf(b, " bound to %s", buf); - eb_printf(b, "\n"); + eb_putc(b, '\n'); /* TODO: print short description */ - eb_printf(b, "\n"); + eb_putc(b, '\n'); found = 1; } d++; @@ -994,7 +1003,7 @@ void do_apropos(EditState *s, const char *str) eb_printf(b, "%s variable: %s -> %s\n", var_domain[vp->domain], vp->name, buf); /* TODO: print short description */ - eb_printf(b, "\n"); + eb_putc(b, '\n'); found = 1; } } @@ -1023,7 +1032,7 @@ static void do_about_qemacs(EditState *s) b = eb_scratch("*About QEmacs*", BF_UTF8); eb_printf(b, "\n %s\n\n%s\n", str_version, str_credits); - /* list commands */ + /* list current bindings */ print_bindings(b, s->mode); print_bindings(b, NULL); @@ -1035,11 +1044,13 @@ static void do_about_qemacs(EditState *s) /* list commands */ eb_printf(b, "\nCommands:\n\n"); + + /* XXX: should sort commands */ d = qs->first_cmd; while (d != NULL) { while (d->name != NULL) { qe_get_prototype(d, buf, sizeof(buf)); - eb_printf(b, " %s(%s);\n", d->name, buf); + eb_printf(b, " %s%s\n", d->name, buf); d++; } d = d->action.next; @@ -1052,6 +1063,7 @@ static void do_about_qemacs(EditState *s) char **envp; eb_printf(b, "\nEnvironment:\n\n"); + /* XXX: should sort environment variables */ for (envp = environ; *envp; envp++) { eb_printf(b, " %s\n", *envp); } @@ -1226,7 +1238,7 @@ static void do_describe_buffer(EditState *s, int argval) if (!b1) return; - eb_printf(b1, "\n"); + eb_putc(b1, '\n'); eb_printf(b1, " name: %s\n", b->name); eb_printf(b1, " filename: %s\n", b->filename); @@ -1370,12 +1382,12 @@ static void do_describe_buffer(EditState *s, int argval) col += eb_printf(b1, " 0x%02x", i); if (col >= 60) { - eb_printf(b1, "\n"); + eb_putc(b1, '\n'); col = 0; } } if (col) { - eb_printf(b1, "\n"); + eb_putc(b1, '\n'); } } @@ -1402,14 +1414,14 @@ static void do_describe_buffer(EditState *s, int argval) if (c < 32 || c >= 127) eb_printf(b1, "\\%03o", c); else - eb_printf(b1, "%c", c); + eb_putc(b1, c); continue; } eb_printf(b1, "\\%c", c); } eb_printf(b1, "|%s\n", p->size > 16 ? "..." : ""); } - eb_printf(b1, "\n"); + eb_putc(b1, '\n'); } b1->flags |= BF_READONLY; @@ -1425,7 +1437,7 @@ static void do_describe_window(EditState *s, int argval) if (!b1) return; - eb_printf(b1, "\n"); + eb_putc(b1, '\n'); w = 28; eb_printf(b1, "%*s: %d, %d\n", w, "xleft, ytop", s->xleft, s->ytop); @@ -1479,7 +1491,7 @@ static void do_describe_window(EditState *s, int argval) eb_printf(b1, "%*s: %d\n", w, "show_selection", s->show_selection); eb_printf(b1, "%*s: %d\n", w, "region_style", s->region_style); eb_printf(b1, "%*s: %d\n", w, "curline_style", s->curline_style); - eb_printf(b1, "\n"); + eb_putc(b1, '\n'); b1->flags |= BF_READONLY; show_popup(s, b1, "Window Description"); @@ -1495,7 +1507,7 @@ static void do_describe_screen(EditState *e, int argval) if (!b1) return; - eb_printf(b1, "\n"); + eb_putc(b1, '\n'); w = 16; eb_printf(b1, "%*s: %s\n", w, "dpy.name", s->dpy.name); @@ -1627,6 +1639,8 @@ static void do_sort_span(EditState *s, int p1, int p2, int flags, int argval) { struct chunk *chunk_array; s->region_style = 0; + if (argval != NO_ARG) + flags |= argval; if (p1 > p2) { int tmp = p1; @@ -1634,8 +1648,6 @@ static void do_sort_span(EditState *s, int p1, int p2, int flags, int argval) { p2 = tmp; } ctx.b = s->b; - if (argval != NO_ARG) - flags |= argval; ctx.flags = flags; ctx.col = 0; eb_get_pos(s->b, &line1, &col1, p1); /* line1 is included */ @@ -1703,7 +1715,7 @@ static void tag_buffer(EditState *s) { } } -static void tag_completion(CompleteState *cp) { +static void tag_complete(CompleteState *cp) { /* XXX: only support current buffer */ QEProperty *p; @@ -1876,6 +1888,10 @@ static CmdDef extra_commands[] = { CMD_DEF_END, }; +static CompletionDef tag_completion = { + "tag", tag_complete, +}; + static int extras_init(void) { int key; @@ -1884,7 +1900,7 @@ static int extras_init(void) for (key = KEY_META('0'); key <= KEY_META('9'); key++) { qe_register_binding(key, "numeric-argument", NULL); } - register_completion("tag", tag_completion); + qe_register_completion(&tag_completion); return 0; } diff --git a/image.c b/image.c index 17a9bde..e9f08ea 100644 --- a/image.c +++ b/image.c @@ -846,7 +846,7 @@ void image_mode_line(EditState *s, buf_t *out) ib->interleaved ? 'I' : ' '); } -static void pixel_format_completion(CompleteState *cp) +static void pixel_format_complete(CompleteState *cp) { int i; const char *name; @@ -863,7 +863,7 @@ static CmdDef image_commands[] = { "image-rotate", image_rotate) CMD2( 'c', KEY_NONE, "image-convert", image_convert, ESs, - "s{New pixel format: }[pixel_format]|pixel_format|") + "s{New pixel format: }[pixel-format]|pixel-format|") CMD2( 'b', KEY_NONE, "image-set-background-color", image_set_background_color, ESs, "s{Background color (use 'transparent' for tiling): }") @@ -909,13 +909,17 @@ static ModeDef image_mode = { .get_mode_line = image_mode_line, }; +static CompletionDef pixel_format_completion = { + "pixel-format", pixel_format_complete +}; + static int image_init(void) { av_register_all(); eb_register_data_type(&image_data_type); qe_register_mode(&image_mode, MODEF_DATATYPE | MODEF_VIEW); qe_register_cmd_table(image_commands, &image_mode); - register_completion("pixel_format", pixel_format_completion); + qe_register_completion(&pixel_format_completion); /* additional mode specific keys */ qe_register_binding('f', "toggle-full-screen", &image_mode); return 0; diff --git a/input.c b/input.c index 7a6a954..28867ca 100644 --- a/input.c +++ b/input.c @@ -80,7 +80,7 @@ void register_input_method(InputMethod *m) *p = m; } -static void input_completion(CompleteState *cp) +static void input_complete(CompleteState *cp) { QEmacsState *qs = cp->s->qe_state; InputMethod *m; @@ -122,9 +122,13 @@ void do_switch_input_method(EditState *s) s->input_method = s->selected_input_method; } +static CompletionDef input_completion = { + "input", input_complete, +}; + void init_input_methods(void) { register_input_method(&default_input_method); register_input_method(&unicode_input_method); - register_completion("input", input_completion); + qe_register_completion(&input_completion); } diff --git a/latex-mode.c b/latex-mode.c index 50bc48c..b919703 100644 --- a/latex-mode.c +++ b/latex-mode.c @@ -211,7 +211,7 @@ static struct latex_function { #undef INIT_TAIL }; -static void latex_completion(CompleteState *cp) +static void latex_complete(CompleteState *cp) { struct latex_function *func; @@ -344,12 +344,16 @@ static ModeDef texinfo_mode = { .colorize_flags = TEX_TEXINFO, }; +static CompletionDef latex_completion = { + "latex", latex_complete, +}; + static int latex_init(void) { qe_register_mode(&latex_mode, MODEF_SYNTAX); qe_register_mode(&texinfo_mode, MODEF_SYNTAX); qe_register_cmd_table(latex_commands, &latex_mode); - register_completion("latex", latex_completion); + qe_register_completion(&latex_completion); return 0; } diff --git a/qe.c b/qe.c index a7db336..544f7d8 100644 --- a/qe.c +++ b/qe.c @@ -62,7 +62,6 @@ static void display1(DisplayState *ds); #ifndef CONFIG_TINY static void save_selection(void); #endif -static CompletionFunc find_completion(const char *name); QEmacsState qe_state; /* should handle multiple screens, and multiple sessions */ @@ -206,7 +205,7 @@ void qe_register_mode(ModeDef *m, int flags) } } -void mode_completion(CompleteState *cp) +void mode_complete(CompleteState *cp) { QEmacsState *qs = cp->s->qe_state; ModeDef *m; @@ -218,6 +217,10 @@ void mode_completion(CompleteState *cp) } } +static CompletionDef mode_completion = { + "mode", mode_complete +}; + /* commands handling */ CmdDef *qe_find_cmd(const char *cmd_name) @@ -237,7 +240,7 @@ CmdDef *qe_find_cmd(const char *cmd_name) return NULL; } -void command_completion(CompleteState *cp) +void command_complete(CompleteState *cp) { QEmacsState *qs = cp->s->qe_state; CmdDef *d; @@ -252,6 +255,51 @@ void command_completion(CompleteState *cp) } } +int command_print_entry(EditState *s, const char *name) +{ + EditBuffer *b = s->b; + CmdDef *d = qe_find_cmd(name); + if (d) { + char buf[256]; + int len; + + b->cur_style = QE_STYLE_FUNCTION; + len = eb_puts(b, d->name); + b->cur_style = QE_STYLE_DEFAULT; + qe_get_prototype(d, buf, sizeof buf); + len += eb_puts(b, buf); +#ifndef CONFIG_TINY + if (qe_list_bindings(d, s->mode, 1, buf, sizeof buf)) { + b->cur_style = QE_STYLE_COMMENT; + if (2 + len < 40) { + b->tab_width = max(2 + len, b->tab_width); + len += eb_putc(b, '\t'); + } else { + b->tab_width = 40; + } + len += eb_printf(b, " bound to %s", buf); + b->cur_style = QE_STYLE_DEFAULT; + } +#endif + return len; + } else { + return eb_puts(b, name); + } +} + +int command_get_entry(EditState *s, char *dest, int size, int offset) +{ + int len; + eb_fgets(s->b, dest, size, offset, &offset); + len = strcspn(dest, " \t\n("); + dest[len] = '\0'; /* strip the TAB or trailing newline if any */ + return len; +} + +static CompletionDef command_completion = { + "command", command_complete, command_print_entry, command_get_entry +}; + static int qe_register_binding1(unsigned int *keys, int nb_keys, CmdDef *d, ModeDef *m) { @@ -2958,7 +3006,7 @@ void get_style(EditState *e, QEStyleDef *stp, QETermStyle style) apply_style(stp, style); } -void style_completion(CompleteState *cp) +void style_complete(CompleteState *cp) { int i; QEStyleDef *stp; @@ -2997,6 +3045,10 @@ QEStyleDef *find_style(const char *name) return NULL; } +static CompletionDef style_completion = { + "style", style_complete +}; + static const char * const qe_style_properties[] = { #define CSS_PROP_COLOR 0 "color", /* color */ @@ -3015,7 +3067,7 @@ static const char * const qe_style_properties[] = { "text-decoration", /* text_decoration: none / underline */ }; -void style_property_completion(CompleteState *cp) +void style_property_complete(CompleteState *cp) { int i; @@ -3024,6 +3076,10 @@ void style_property_completion(CompleteState *cp) } } +static CompletionDef style_property_completion = { + "style-property", style_property_complete +}; + int find_style_property(const char *name) { int i; @@ -4721,6 +4777,8 @@ int qe_get_prototype(CmdDef *d, char *buf, int size) out = buf_init(&outbuf, buf, size); + buf_put_byte(out, '('); + /* construct argument type list */ r = d->name + strlen(d->name) + 1; if (*r == '*') { @@ -4763,6 +4821,7 @@ int qe_get_prototype(CmdDef *d, char *buf, int size) buf_puts(out, *history ? history : completion); } } + buf_put_byte(out, ')'); return out->len; } @@ -4874,6 +4933,7 @@ static void parse_args(ExecCmdState *es) char def_input[1024]; /* XXX: currently, default input is handled non generically */ + /* XXX: should use completion function for default input? */ def_input[0] = '\0'; es->default_input[0] = '\0'; if (strequal(completion_name, "file")) { @@ -5010,6 +5070,7 @@ void do_execute_command(EditState *s, const char *cmd, int argval) { CmdDef *d; + /* XXX: should test for '(' and '=' and evaluate script instead */ d = qe_find_cmd(cmd); if (d) { exec_command(s, d, argval, 0); @@ -5917,7 +5978,7 @@ static const char *file_completion_ignore_extensions = { "|" }; -void file_completion(CompleteState *cp) +void file_complete(CompleteState *cp) { char path[MAX_FILENAME_SIZE]; char file[MAX_FILENAME_SIZE]; @@ -5969,7 +6030,11 @@ void file_completion(CompleteState *cp) find_file_close(&ffst); } -void buffer_completion(CompleteState *cp) +static CompletionDef file_completion = { + "file", file_complete +}; + +void buffer_complete(CompleteState *cp) { QEmacsState *qs = cp->s->qe_state; EditBuffer *b; @@ -5980,45 +6045,69 @@ void buffer_completion(CompleteState *cp) } } -/* register a new completion method */ -void register_completion(const char *name, CompletionFunc completion_func) -{ - QEmacsState *qs = &qe_state; - CompletionEntry **lp, *p; +static CompletionDef buffer_completion = { + "buffer", buffer_complete +}; - p = qe_mallocz(CompletionEntry); - if (!p) - return; - - p->name = name; - p->completion_func = completion_func; - p->next = NULL; - - lp = &qs->first_completion; - while (*lp != NULL) - lp = &(*lp)->next; - *lp = p; +static int default_completion_window_print_entry(EditState *s, const char *name) { + return eb_puts(s->b, name); } -static CompletionFunc find_completion(const char *name) +static int default_completion_window_get_entry(EditState *s, char *dest, int size, int offset) { + int len = eb_fgets(s->b, dest, size, offset, &offset); + char *p = strchr(dest, '\t'); + if (p != NULL) + len = p - dest; + dest[len] = '\0'; /* strip the TAB or trailing newline if any */ + return len; +} + +/* register a new completion method */ +void qe_register_completion(CompletionDef *cp) { - CompletionEntry *p; + QEmacsState *qs = &qe_state; + CompletionDef **p; + + for (p = &qs->first_completion;; p = &(*p)->next) { + if (*p == cp) { + /* completion is already registered, do nothing */ + return; + } + if (*p == NULL) { + cp->next = NULL; + *p = cp; + break; + } + } + if (!cp->print_entry) + cp->print_entry = default_completion_window_print_entry; + if (!cp->get_entry) + cp->get_entry = default_completion_window_get_entry; +} + +static CompletionDef *find_completion(const char *name) +{ + CompletionDef *p; if (name[0] != '\0') { for (p = qe_state.first_completion; p != NULL; p = p->next) { if (strequal(p->name, name)) - return p->completion_func; + return p; } } return NULL; } -static void complete_start(CompleteState *cp, EditState *s, EditState *target) +static void complete_start(CompleteState *cp, EditState *s, int start, int end, + EditState *target) { memset(cp, 0, sizeof(*cp)); cp->s = s; cp->target = target; - cp->len = eb_get_contents(s->b, cp->current, sizeof(cp->current)); + cp->start = start; + cp->end = end; + cp->len = eb_get_region_contents(s->b, cp->start, cp->end, + cp->current, sizeof(cp->current)); } /* XXX: should have a globbing option */ @@ -6065,8 +6154,10 @@ typedef struct MinibufState { void *opaque; EditState *completion_popup_window; /* XXX: should have a popup_window member */ - OWNED char *completion_name; - CompletionFunc completion_function; + int completion_flags; + int completion_start; + int completion_end; + CompletionDef *completion; StringArray *history; int history_index; @@ -6082,7 +6173,7 @@ static inline MinibufState *minibuffer_get_state(EditState *e, int status) { void do_minibuffer_complete(EditState *s, int type) { QEmacsState *qs = s->qe_state; - int count, i, match_len, c; + int count, i, match_len, c, start, end; CompleteState cs; StringItem **outputs; EditState *e; @@ -6093,7 +6184,7 @@ void do_minibuffer_complete(EditState *s, int type) if ((mb = minibuffer_get_state(s, 1)) == NULL) return; - if (!mb->completion_function) + if (!mb->completion) return; /* Remove highlighted selection. */ @@ -6117,8 +6208,21 @@ void do_minibuffer_complete(EditState *s, int type) return; } - complete_start(&cs, s, s->target_window); - (*mb->completion_function)(&cs); + start = 0; + end = s->offset; + if (mb->completion_flags) { + /* XXX: completion select? */ + int offset = end; + while ((start = offset) > 0) { + int c = eb_prevc(s->b, offset, &offset); + if (!qe_isalnum_(c) && c != '-') + break; + } + } + mb->completion_start = start; + mb->completion_end = end; + complete_start(&cs, s, start, end, s->target_window); + (*mb->completion->enumerate)(&cs); count = cs.cs.nb_items; outputs = cs.cs.items; #if 0 @@ -6150,10 +6254,12 @@ void do_minibuffer_complete(EditState *s, int type) if (match_len > cs.len) { /* add the possible chars */ // XXX: potential utf-8 issue? - eb_write(s->b, 0, outputs[0]->str, match_len); - s->offset = match_len; + // XXX: replace the completed part, not necessarily at the start (use mark?) + // XXX: should delete region and insert as utf-8 + eb_replace(s->b, cs.start, cs.end - cs.start, outputs[0]->str, match_len); + s->offset = cs.start + match_len; if (type == COMPLETION_OTHER) { - do_mark_region(s, match_len, cs.len); + do_mark_region(s, cs.start + match_len, cs.start + cs.len); // XXX: ??? } } else { if (count > 1) { @@ -6170,7 +6276,7 @@ void do_minibuffer_complete(EditState *s, int type) w = (w1 * 3) / 4; h = (h1 * 3) / 4; e = edit_new(b, (w1 - w) / 2, (h1 - h) / 2, w, h, WF_POPUP); - snprintf(buf, sizeof buf, "Select a %s:", mb->completion_name); + snprintf(buf, sizeof buf, "Select a %s:", mb->completion->name); e->caption = qe_strdup(buf); e->target_window = s; mb->completion_popup_window = e; @@ -6190,10 +6296,12 @@ void do_minibuffer_complete(EditState *s, int type) qsort(outputs, count, sizeof(StringItem *), completion_sort_func); b->flags &= ~BF_READONLY; eb_delete(b, 0, b->total_size); + b->tab_width = 4; for (i = 0; i < count; i++) { - eb_printf(b, " %s", outputs[i]->str); + eb_putc(b, ' '); /* XXX: should use window margins */ + mb->completion->print_entry(e, outputs[i]->str); if (i != count - 1) - eb_printf(b, "\n"); + eb_putc(b, '\n'); } b->flags |= BF_READONLY; e->mouse_force_highlight = 1; @@ -6221,7 +6329,7 @@ void do_minibuffer_electric(EditState *s, int key) int c, offset, stop; MinibufState *mb = minibuffer_get_state(s, 0); - if (mb && mb->completion_function == file_completion) { + if (mb && mb->completion == &file_completion) { stop = s->offset; c = eb_prevc(s->b, s->offset, &offset); if (c == '/') { @@ -6242,7 +6350,7 @@ void do_minibuffer_complete_space(EditState *s) QEmacsState *qs = s->qe_state; MinibufState *mb = minibuffer_get_state(s, 0); - if (!mb || !mb->completion_function) { + if (!mb || !mb->completion) { do_char(s, ' ', 1); } else if (check_window(&mb->completion_popup_window) @@ -6278,15 +6386,15 @@ void minibuf_complete_scroll_up_down(qe__unused__ EditState *s, int dir) } } -static void minibuf_set_str(EditState *s, const char *str) +static void minibuf_set_str(EditState *s, int start, int end, const char *str) { int len; - eb_delete(s->b, 0, s->b->total_size); + /* Replace the completion trigger zone */ /* XXX: should insert utf-8? */ len = strlen(str); - eb_write(s->b, 0, str, len); - s->offset = len; + eb_replace(s->b, start, end - start, str, len); + s->offset = start + len; } /* CG: should use buffer of responses */ @@ -6347,7 +6455,7 @@ void do_minibuffer_history(EditState *s, int dir) /* insert history text */ mb->history_index = index; str = hist->items[index]->str; - minibuf_set_str(s, str); + minibuf_set_str(s, 0, s->b->total_size, str); if (index == hist->nb_items - 1) { s->offset = mb->history_saved_offset; } @@ -6383,12 +6491,11 @@ void do_minibuffer_exit(EditState *s, int do_abort) /* if completion is activated, then select current file only if the selection is highlighted */ if (cw && cw->force_highlight) { - int offset, len; - - len = eb_fgets(cw->b, buf, sizeof(buf), list_get_offset(cw), &offset); - buf[len] = '\0'; /* strip the trailing newline if any */ + int len; + len = mb->completion->get_entry(cw, buf, sizeof(buf), list_get_offset(cw) + 1); if (len > 0) { - minibuf_set_str(s, buf + 1); + eb_delete_range(s->b, s->b->mark, s->offset); // delete highlighted completion + minibuf_set_str(s, mb->completion_start, mb->completion_end, buf); } } @@ -6417,7 +6524,6 @@ void do_minibuffer_exit(EditState *s, int do_abort) target = s->target_window; mb->cb = NULL; mb->opaque = NULL; - qe_free(&mb->completion_name); /* Close the minibuffer window */ s->b->flags |= BF_TRANSIENT; @@ -6477,11 +6583,13 @@ void minibuffer_edit(EditState *e, const char *input, const char *prompt, mb = minibuffer_get_state(s, 0); if (mb) { mb->completion_popup_window = NULL; - mb->completion_name = NULL; - mb->completion_function = NULL; + mb->completion = NULL; if (completion_name) { - mb->completion_name = qe_strdup(completion_name); - mb->completion_function = find_completion(completion_name); + if (*completion_name == '.') { + mb->completion_flags = 1; + completion_name++; + } + mb->completion = find_completion(completion_name); } mb->history = hist; mb->history_saved_offset = 0; @@ -8039,13 +8147,13 @@ void do_help_for_help(EditState *s) if (!b) return; - eb_printf(b, - "QEmacs help for help - Press q to quit:\n" - "\n" - "C-h C-h Show this help\n" - "C-h b Display table of all key bindings\n" - "C-h c Describe key briefly\n" - ); + eb_puts(b, + "QEmacs help for help - Press q to quit:\n" + "\n" + "C-h C-h Show this help\n" + "C-h b Display table of all key bindings\n" + "C-h c Describe key briefly\n" + ); b->flags |= BF_READONLY; show_popup(s, b, "QEmacs help for help - Press q to quit:"); } @@ -8952,6 +9060,14 @@ typedef struct QEArgs { char **argv; } QEArgs; +static CompletionDef charset_completion = { + "charset", charset_complete +}; + +static CompletionDef color_completion = { + "color", color_complete +}; + /* init function */ static void qe_init(void *opaque) { @@ -8998,14 +9114,14 @@ static void qe_init(void *opaque) qe_register_cmd_table(basic_commands, NULL); qe_register_cmd_line_options(cmd_options); - register_completion("command", command_completion); - register_completion("charset", charset_completion); - register_completion("mode", mode_completion); - register_completion("style", style_completion); - register_completion("style-property", style_property_completion); - register_completion("file", file_completion); - register_completion("buffer", buffer_completion); - register_completion("color", color_completion); + qe_register_completion(&command_completion); + qe_register_completion(&charset_completion); + qe_register_completion(&mode_completion); + qe_register_completion(&style_completion); + qe_register_completion(&style_property_completion); + qe_register_completion(&file_completion); + qe_register_completion(&buffer_completion); + qe_register_completion(&color_completion); minibuffer_init(); popup_init(); @@ -9216,7 +9332,7 @@ int main(int argc, char **argv) } } while (qs->first_completion) { - CompletionEntry *cp = qs->first_completion; + CompletionDef *cp = qs->first_completion; qs->first_completion = cp->next; qe_free(&cp); } diff --git a/qe.h b/qe.h index 456822c..4b0b06b 100644 --- a/qe.h +++ b/qe.h @@ -194,7 +194,7 @@ typedef struct CompleteState { StringArray cs; struct EditState *s; struct EditState *target; - int len; + int start, end, len; char current[MAX_FILENAME_SIZE]; } CompleteState; @@ -502,7 +502,7 @@ unsigned int qe_map_color(QEColor color, QEColor const *colors, int count, int * /* Convert a composite color to an RGB triplet */ QEColor qe_unmap_color(int color, int count); -void color_completion(CompleteState *cp); +void color_complete(CompleteState *cp); int css_define_color(const char *name, const char *value); int css_get_color(QEColor *color_ptr, const char *p); void css_free_colors(void); @@ -810,7 +810,7 @@ int utf8_decode(const char **pp); char *utf8_char_to_string(char *buf, int c); int utf8_to_unicode(unsigned int *dest, int dest_length, const char *str); -void charset_completion(CompleteState *cp); +void charset_complete(CompleteState *cp); QECharset *find_charset(const char *str); void charset_decode_init(CharsetDecodeState *s, QECharset *charset, EOLType eol_type); @@ -1637,7 +1637,7 @@ struct QEmacsState { struct ModeDef *first_mode; struct KeyDef *first_key; struct CmdDef *first_cmd; - struct CompletionEntry *first_completion; + struct CompletionDef *first_completion; struct HistoryEntry *first_history; //struct QECharset *first_charset; //struct QETimer *first_timer; @@ -1819,11 +1819,12 @@ typedef struct CmdDef { ModeDef *qe_find_mode(const char *name, int flags); ModeDef *qe_find_mode_filename(const char *filename, int flags); void qe_register_mode(ModeDef *m, int flags); -void mode_completion(CompleteState *cp); +void mode_complete(CompleteState *cp); void qe_register_cmd_table(CmdDef *cmds, ModeDef *m); int qe_register_binding(int key, const char *cmd_name, ModeDef *m); CmdDef *qe_find_cmd(const char *cmd_name); int qe_get_prototype(CmdDef *d, char *buf, int size); +int qe_list_bindings(CmdDef *d, ModeDef *mode, int inherit, char *buf, int size); /* text display system */ @@ -1998,25 +1999,29 @@ void minibuffer_init(void); extern CmdDef minibuffer_commands[]; extern CmdDef popup_commands[]; -typedef void (*CompletionFunc)(CompleteState *cp); - -typedef struct CompletionEntry { +typedef struct CompletionDef { const char *name; - CompletionFunc completion_func; - struct CompletionEntry *next; -} CompletionEntry; + void (*enumerate)(CompleteState *cp); + int (*print_entry)(EditState *s, const char *name); + int (*get_entry)(EditState *s, char *dest, int size, int offset); + int flags; + struct CompletionDef *next; +} CompletionDef; + +void qe_register_completion(CompletionDef *cp); void complete_test(CompleteState *cp, const char *str); -void register_completion(const char *name, CompletionFunc completion_func); void put_status(EditState *s, const char *fmt, ...) qe__attr_printf(2,3); void put_error(EditState *s, const char *fmt, ...) qe__attr_printf(2,3); void minibuffer_edit(EditState *e, const char *input, const char *prompt, StringArray *hist, const char *completion_name, void (*cb)(void *opaque, char *buf), void *opaque); -void command_completion(CompleteState *cp); -void file_completion(CompleteState *cp); -void buffer_completion(CompleteState *cp); +void command_complete(CompleteState *cp); +int command_print_entry(EditState *s, const char *name); +int command_get_entry(EditState *s, char *dest, int size, int offset); +void file_complete(CompleteState *cp); +void buffer_complete(CompleteState *cp); #ifdef CONFIG_WIN32 static inline int is_user_input_pending(void) { @@ -2223,9 +2228,9 @@ void do_set_indent_tabs_mode(EditState *s, int val); void display_window_borders(EditState *e); int find_style_index(const char *name); QEStyleDef *find_style(const char *name); -void style_completion(CompleteState *cp); +void style_complete(CompleteState *cp); void get_style(EditState *e, QEStyleDef *stp, QETermStyle style); -void style_property_completion(CompleteState *cp); +void style_property_complete(CompleteState *cp); int find_style_property(const char *name); void do_define_color(EditState *e, const char *name, const char *value); void do_set_style(EditState *e, const char *stylestr, diff --git a/qscript.c b/qscript.c index 7e23ddc..261c425 100644 --- a/qscript.c +++ b/qscript.c @@ -112,6 +112,7 @@ static inline void qe_cfg_set_str(QEValue *sp, const char *str, int len) { sp->u.str = qe_malloc_array(char, len + 1); memcpy(sp->u.str, str, len); sp->u.str[len] = '\0'; + sp->len = len; sp->type = TOK_STRING; } @@ -1163,11 +1164,29 @@ void do_save_session(EditState *s, int popup) } #endif +void script_complete(CompleteState *cp) { + command_complete(cp); + variable_complete(cp); +} + +int script_print_entry(EditState *s, const char *name) { + CmdDef *d = qe_find_cmd(name); + if (d) { + return command_print_entry(s, name); + } else { + return variable_print_entry(s, name); + } +} + +static CompletionDef script_completion = { + "script", script_complete, script_print_entry, command_get_entry +}; + static CmdDef parser_commands[] = { CMD2( KEY_META(':'), KEY_NONE, "eval-expression", do_eval_expression, ESsi, - "s{Eval: }|expression|ui") + "s{Eval: }[.script]|expression|ui") CMD0( KEY_NONE, KEY_NONE, "eval-region", do_eval_region) CMD0( KEY_NONE, KEY_NONE, @@ -1182,6 +1201,7 @@ static CmdDef parser_commands[] = { static int parser_init(void) { qe_register_cmd_table(parser_commands, NULL); + qe_register_completion(&script_completion); return 0; } diff --git a/tty.c b/tty.c index 8bca21c..dd11de6 100644 --- a/tty.c +++ b/tty.c @@ -906,7 +906,7 @@ static void comb_cache_describe(QEditScreen *s, EditBuffer *b) { eb_printf(b, "%*s: fg:%d, bg:%d\n", w, "virtual tty colors", ts->tty_fg_colors_count, ts->tty_bg_colors_count); - eb_printf(b, "\n"); + eb_putc(b, '\n'); eb_printf(b, "Unicode combination cache:\n\n"); for (ip = ts->comb_cache; *ip != 0; ip += *ip & 0xFFFF) { @@ -919,7 +919,7 @@ static void comb_cache_describe(QEditScreen *s, EditBuffer *b) { for (i = 1; i < (*ip & 0xFFFF); i++) { eb_printf(b, " %04X", ip[i]); } - eb_printf(b, "\n"); + eb_putc(b, '\n'); } } } diff --git a/util.c b/util.c index c3a82ce..cae896f 100644 --- a/util.c +++ b/util.c @@ -1811,7 +1811,7 @@ QEColor qe_unmap_color(int color, int count) { return color & 0xFFFFFF; } -void color_completion(CompleteState *cp) +void color_complete(CompleteState *cp) { ColorDef const *def; int count; diff --git a/variables.c b/variables.c index b9b0637..2ba519c 100644 --- a/variables.c +++ b/variables.c @@ -115,7 +115,7 @@ VarDef *qe_find_variable(const char *name) return NULL; } -void qe_complete_variable(CompleteState *cp) +void variable_complete(CompleteState *cp) { QEmacsState *qs = cp->s->qe_state; const VarDef *vp; @@ -369,6 +369,7 @@ void qe_list_variables(EditState *s, EditBuffer *b) eb_puts(b, "\n variables:\n\n"); for (vp = qs->first_variable; vp; vp = vp->next) { + /* XXX: merge with variable_print_entry() */ switch (vp->type) { case VAR_NUMBER: type = "int"; @@ -415,15 +416,67 @@ void qe_save_variables(EditState *s, EditBuffer *b) eb_putc(b, '\n'); } +int variable_print_entry(EditState *s, const char *name) +{ + char buf[256]; + char typebuf[32]; + const char *type = typebuf; + int len; + VarDef *vp = qe_find_variable(name); + EditBuffer *b = s->b; + + len = eb_puts(b, name); + if (!vp) + return len; + + switch (vp->type) { + case VAR_NUMBER: + type = "int"; + break; + case VAR_STRING: + type = "string"; + break; + case VAR_CHARS: + snprintf(typebuf, sizeof(typebuf), "char[%d]", vp->size); + break; + default: + type = "var"; + break; + } + len += eb_printf(b, " = "); + qe_get_variable(s, vp->name, buf, sizeof(buf), NULL, 1); + if (*buf == '\"') + b->cur_style = QE_STYLE_STRING; + else + b->cur_style = QE_STYLE_NUMBER; + len += eb_puts(b, buf); + b->cur_style = QE_STYLE_COMMENT; + if (2 + len < 40) { + b->tab_width = max(2 + len, b->tab_width); + len += eb_putc(b, '\t'); + } else { + b->tab_width = 40; + } + len += eb_printf(b, " %s%s %s", + vp->rw ? "" : "read-only ", + var_domain[vp->domain], type); + b->cur_style = QE_STYLE_DEFAULT; + return len; +} + +static CompletionDef variable_completion = { + "variable", variable_complete, variable_print_entry, command_get_entry +}; + /*---------------- commands ----------------*/ static CmdDef var_commands[] = { CMD2( KEY_NONE, KEY_NONE, "show-variable", do_show_variable, ESs, - "s{Show variable: }[var]|var|") + "s{Show variable: }[variable]|variable|") CMD2( KEY_F8, KEY_NONE, "set-variable", do_set_variable, ESss, - "s{Set variable: }[var]|var|s{to value: }|value|") + "s{Set variable: }[variable]|variable|s{to value: }|value|") CMD_DEF_END, }; @@ -432,7 +485,7 @@ static int vars_init(void) { qe_register_variables(var_table, countof(var_table)); qe_register_cmd_table(var_commands, NULL); - register_completion("var", qe_complete_variable); + qe_register_completion(&variable_completion); return 0; } diff --git a/variables.h b/variables.h index 6dda95a..c3440d8 100644 --- a/variables.h +++ b/variables.h @@ -95,7 +95,8 @@ struct VarDef { void qe_register_variables(VarDef *vars, int count); VarDef *qe_find_variable(const char *name); -void qe_complete_variable(CompleteState *cp); +void variable_complete(CompleteState *cp); +int variable_print_entry(EditState *s, const char *name); QVarType qe_get_variable(EditState *s, const char *name, char *buf, int size, int *pnum, int as_source);