Path: blob/master/extern/isocline/src/editline_completion.c
2727 views
/* ----------------------------------------------------------------------------1Copyright (c) 2021, Daan Leijen2This is free software; you can redistribute it and/or modify it3under the terms of the MIT License. A copy of the license can be4found in the "LICENSE" file at the root of this distribution.5-----------------------------------------------------------------------------*/67//-------------------------------------------------------------8// Completion menu: this file is included in editline.c9//-------------------------------------------------------------1011// return true if anything changed12static bool edit_complete(ic_env_t* env, editor_t* eb, ssize_t idx) {13editor_start_modify(eb);14ssize_t newpos = completions_apply(env->completions, idx, eb->input, eb->pos);15if (newpos < 0) {16editor_undo_restore(eb,false);17return false;18}19eb->pos = newpos;20edit_refresh(env,eb);21return true;22}2324static bool edit_complete_longest_prefix(ic_env_t* env, editor_t* eb ) {25editor_start_modify(eb);26ssize_t newpos = completions_apply_longest_prefix( env->completions, eb->input, eb->pos );27if (newpos < 0) {28editor_undo_restore(eb,false);29return false;30}31eb->pos = newpos;32edit_refresh(env,eb);33return true;34}3536ic_private void sbuf_append_tagged( stringbuf_t* sb, const char* tag, const char* content ) {37sbuf_appendf(sb, "[%s]", tag);38sbuf_append(sb,content);39sbuf_append(sb,"[/]");40}4142static void editor_append_completion(ic_env_t* env, editor_t* eb, ssize_t idx, ssize_t width, bool numbered, bool selected ) {43const char* help = NULL;44const char* display = completions_get_display(env->completions, idx, &help);45if (display == NULL) return;46if (numbered) {47sbuf_appendf(eb->extra, "[ic-info]%s%zd [/]", (selected ? (tty_is_utf8(env->tty) ? "\xE2\x86\x92" : "*") : " "), 1 + idx);48width -= 3;49}5051if (width > 0) {52sbuf_appendf(eb->extra, "[width=\"%zd;left; ;on\"]", width );53}54if (selected) {55sbuf_append(eb->extra, "[ic-emphasis]");56}57sbuf_append(eb->extra, display);58if (selected) { sbuf_append(eb->extra,"[/ic-emphasis]"); }59if (help != NULL) {60sbuf_append(eb->extra, " ");61sbuf_append_tagged(eb->extra, "ic-info", help );62}63if (width > 0) { sbuf_append(eb->extra,"[/width]"); }64}6566// 2 and 3 column output up to 80 wide67#define IC_DISPLAY2_MAX 3468#define IC_DISPLAY2_COL (3+IC_DISPLAY2_MAX)69#define IC_DISPLAY2_WIDTH (2*IC_DISPLAY2_COL + 2) // 757071#define IC_DISPLAY3_MAX 2172#define IC_DISPLAY3_COL (3+IC_DISPLAY3_MAX)73#define IC_DISPLAY3_WIDTH (3*IC_DISPLAY3_COL + 2*2) // 767475static void editor_append_completion2(ic_env_t* env, editor_t* eb, ssize_t col_width, ssize_t idx1, ssize_t idx2, ssize_t selected ) {76editor_append_completion(env, eb, idx1, col_width, true, (idx1 == selected) );77sbuf_append( eb->extra, " ");78editor_append_completion(env, eb, idx2, col_width, true, (idx2 == selected) );79}8081static void editor_append_completion3(ic_env_t* env, editor_t* eb, ssize_t col_width, ssize_t idx1, ssize_t idx2, ssize_t idx3, ssize_t selected ) {82editor_append_completion(env, eb, idx1, col_width, true, (idx1 == selected) );83sbuf_append( eb->extra, " ");84editor_append_completion(env, eb, idx2, col_width, true, (idx2 == selected));85sbuf_append( eb->extra, " ");86editor_append_completion(env, eb, idx3, col_width, true, (idx3 == selected) );87}8889static ssize_t edit_completions_max_width( ic_env_t* env, ssize_t count ) {90ssize_t max_width = 0;91for( ssize_t i = 0; i < count; i++) {92const char* help = NULL;93ssize_t w = bbcode_column_width(env->bbcode, completions_get_display(env->completions, i, &help));94if (help != NULL) {95w += 2 + bbcode_column_width(env->bbcode, help);96}97if (w > max_width) {98max_width = w;99}100}101return max_width;102}103104static void edit_completion_menu(ic_env_t* env, editor_t* eb, bool more_available) {105ssize_t count = completions_count(env->completions);106ssize_t count_displayed = count;107assert(count > 1);108ssize_t selected = (env->complete_nopreview ? 0 : -1); // select first or none109ssize_t percolumn = count;110111again:112// show first 9 (or 8) completions113sbuf_clear(eb->extra);114ssize_t twidth = term_get_width(env->term) - 1;115ssize_t colwidth;116if (count > 3 && ((colwidth = 3 + edit_completions_max_width(env, 9))*3 + 2*2) < twidth) {117// display as a 3 column block118count_displayed = (count > 9 ? 9 : count);119percolumn = 3;120for (ssize_t rw = 0; rw < percolumn; rw++) {121if (rw > 0) sbuf_append(eb->extra, "\n");122editor_append_completion3(env, eb, colwidth, rw, percolumn+rw, (2*percolumn)+rw, selected);123}124}125else if (count > 4 && ((colwidth = 3 + edit_completions_max_width(env, 8))*2 + 2) < twidth) {126// display as a 2 column block if some entries are too wide for three columns127count_displayed = (count > 8 ? 8 : count);128percolumn = (count_displayed <= 6 ? 3 : 4);129for (ssize_t rw = 0; rw < percolumn; rw++) {130if (rw > 0) sbuf_append(eb->extra, "\n");131editor_append_completion2(env, eb, colwidth, rw, percolumn+rw, selected);132}133}134else {135// display as a list136count_displayed = (count > 9 ? 9 : count);137percolumn = count_displayed;138for (ssize_t i = 0; i < count_displayed; i++) {139if (i > 0) sbuf_append(eb->extra, "\n");140editor_append_completion(env, eb, i, -1, true /* numbered */, selected == i);141}142}143if (count > count_displayed) {144if (more_available) {145sbuf_append(eb->extra, "\n[ic-info](press page-down (or ctrl-j) to see all further completions)[/]");146}147else {148sbuf_appendf(eb->extra, "\n[ic-info](press page-down (or ctrl-j) to see all %zd completions)[/]", count );149}150}151if (!env->complete_nopreview && selected >= 0 && selected <= count_displayed) {152edit_complete(env,eb,selected);153editor_undo_restore(eb,false);154}155else {156edit_refresh(env, eb);157}158159// read here; if not a valid key, push it back and return to main event loop160code_t c = tty_read(env->tty);161if (tty_term_resize_event(env->tty)) {162edit_resize(env, eb);163}164sbuf_clear(eb->extra);165166// direct selection?167if (c >= '1' && c <= '9') {168ssize_t i = (c - '1');169if (i < count) {170selected = i;171c = KEY_ENTER;172}173}174175// process commands176if (c == KEY_DOWN || c == KEY_TAB) {177selected++;178if (selected >= count_displayed) {179//term_beep(env->term);180selected = 0;181}182goto again;183}184else if (c == KEY_UP || c == KEY_SHIFT_TAB) {185selected--;186if (selected < 0) {187selected = count_displayed - 1;188//term_beep(env->term);189}190goto again;191}192else if (c == KEY_F1) {193edit_show_help(env, eb);194goto again;195}196else if (c == KEY_ESC) {197completions_clear(env->completions);198edit_refresh(env,eb);199c = 0; // ignore and return200}201else if (selected >= 0 && (c == KEY_ENTER || c == KEY_RIGHT || c == KEY_END)) /* || c == KEY_TAB*/ {202// select the current entry203assert(selected < count);204c = 0;205edit_complete(env, eb, selected);206if (env->complete_autotab) {207tty_code_pushback(env->tty,KEY_EVENT_AUTOTAB); // immediately try to complete again208}209}210else if (!env->complete_nopreview && !code_is_virt_key(c)) {211// if in preview mode, select the current entry and exit the menu212assert(selected < count);213edit_complete(env, eb, selected);214}215else if ((c == KEY_PAGEDOWN || c == KEY_LINEFEED) && count > 9) {216// show all completions217c = 0;218if (more_available) {219// generate all entries (up to the max (= 1000))220count = completions_generate(env, env->completions, sbuf_string(eb->input), eb->pos, IC_MAX_COMPLETIONS_TO_SHOW);221}222rowcol_t rc;223edit_get_rowcol(env,eb,&rc);224edit_clear(env,eb);225edit_write_prompt(env,eb,0,false);226term_writeln(env->term, "");227for(ssize_t i = 0; i < count; i++) {228const char* display = completions_get_display(env->completions, i, NULL);229if (display != NULL) {230bbcode_println(env->bbcode, display);231}232}233if (count >= IC_MAX_COMPLETIONS_TO_SHOW) {234bbcode_println(env->bbcode, "[ic-info]... and more.[/]");235}236else {237bbcode_printf(env->bbcode, "[ic-info](%zd possible completions)[/]\n", count );238}239for(ssize_t i = 0; i < rc.row+1; i++) {240term_write(env->term, " \n");241}242eb->cur_rows = 0;243edit_refresh(env,eb);244}245else {246edit_refresh(env,eb);247}248// done249completions_clear(env->completions);250if (c != 0) tty_code_pushback(env->tty,c);251}252253static void edit_generate_completions(ic_env_t* env, editor_t* eb, bool autotab) {254debug_msg( "edit: complete: %zd: %s\n", eb->pos, sbuf_string(eb->input) );255if (eb->pos < 0) return;256ssize_t count = completions_generate(env, env->completions, sbuf_string(eb->input), eb->pos, IC_MAX_COMPLETIONS_TO_TRY);257bool more_available = (count >= IC_MAX_COMPLETIONS_TO_TRY);258if (count <= 0) {259// no completions260if (!autotab) { term_beep(env->term); }261}262else if (count == 1) {263// complete if only one match264if (edit_complete(env,eb,0 /*idx*/) && env->complete_autotab) {265tty_code_pushback(env->tty,KEY_EVENT_AUTOTAB);266}267}268else {269//term_beep(env->term);270if (!more_available) {271edit_complete_longest_prefix(env,eb);272}273completions_sort(env->completions);274edit_completion_menu( env, eb, more_available);275}276}277278279