diff options
Diffstat (limited to 'src/edit.c')
-rw-r--r-- | src/edit.c | 4018 |
1 files changed, 52 insertions, 3966 deletions
diff --git a/src/edit.c b/src/edit.c index dcead23f54..0b5d0e8086 100644 --- a/src/edit.c +++ b/src/edit.c @@ -13,210 +13,18 @@ #include "vim.h" -#ifdef FEAT_INS_EXPAND -/* - * Definitions used for CTRL-X submode. - * Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] and - * ctrl_x_mode_names[]. - */ -# define CTRL_X_WANT_IDENT 0x100 - -# define CTRL_X_NORMAL 0 /* CTRL-N CTRL-P completion, default */ -# define CTRL_X_NOT_DEFINED_YET 1 -# define CTRL_X_SCROLL 2 -# define CTRL_X_WHOLE_LINE 3 -# define CTRL_X_FILES 4 -# define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT) -# define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT) -# define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT) -# define CTRL_X_FINISHED 8 -# define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT) -# define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT) -# define CTRL_X_CMDLINE 11 -# define CTRL_X_FUNCTION 12 -# define CTRL_X_OMNI 13 -# define CTRL_X_SPELL 14 -# define CTRL_X_LOCAL_MSG 15 /* only used in "ctrl_x_msgs" */ -# define CTRL_X_EVAL 16 /* for builtin function complete() */ - -# define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] -# define CTRL_X_MODE_LINE_OR_EVAL(m) ((m) == CTRL_X_WHOLE_LINE || (m) == CTRL_X_EVAL) - -// Message for CTRL-X mode, index is ctrl_x_mode. -static char *ctrl_x_msgs[] = -{ - N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl. - N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), - NULL, // CTRL_X_SCROLL: depends on state - N_(" Whole line completion (^L^N^P)"), - N_(" File name completion (^F^N^P)"), - N_(" Tag completion (^]^N^P)"), - N_(" Path pattern completion (^N^P)"), - N_(" Definition completion (^D^N^P)"), - NULL, // CTRL_X_FINISHED - N_(" Dictionary completion (^K^N^P)"), - N_(" Thesaurus completion (^T^N^P)"), - N_(" Command-line completion (^V^N^P)"), - N_(" User defined completion (^U^N^P)"), - N_(" Omni completion (^O^N^P)"), - N_(" Spelling suggestion (s^N^P)"), - N_(" Keyword Local completion (^N^P)"), - NULL, // CTRL_X_EVAL doesn't use msg. -}; - -static char *ctrl_x_mode_names[] = { - "keyword", - "ctrl_x", - "unknown", // CTRL_X_SCROLL - "whole_line", - "files", - "tags", - "path_patterns", - "path_defines", - "unknown", // CTRL_X_FINISHED - "dictionary", - "thesaurus", - "cmdline", - "function", - "omni", - "spell", - NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" - "eval" - }; - - -static char e_hitend[] = N_("Hit end of paragraph"); -# ifdef FEAT_COMPL_FUNC -static char e_complwin[] = N_("E839: Completion function changed window"); -static char e_compldel[] = N_("E840: Completion function deleted text"); -# endif - -/* - * Structure used to store one match for insert completion. - */ -typedef struct compl_S compl_T; -struct compl_S -{ - compl_T *cp_next; - compl_T *cp_prev; - char_u *cp_str; /* matched text */ - char cp_icase; /* TRUE or FALSE: ignore case */ - char_u *(cp_text[CPT_COUNT]); /* text for the menu */ - char_u *cp_fname; /* file containing the match, allocated when - * cp_flags has FREE_FNAME */ - int cp_flags; /* ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME */ - int cp_number; /* sequence number */ -}; - -# define ORIGINAL_TEXT (1) /* the original text when the expansion begun */ -# define FREE_FNAME (2) - -/* - * All the current matches are stored in a list. - * "compl_first_match" points to the start of the list. - * "compl_curr_match" points to the currently selected entry. - * "compl_shown_match" is different from compl_curr_match during - * ins_compl_get_exp(). - */ -static compl_T *compl_first_match = NULL; -static compl_T *compl_curr_match = NULL; -static compl_T *compl_shown_match = NULL; -static compl_T *compl_old_match = NULL; - -/* After using a cursor key <Enter> selects a match in the popup menu, - * otherwise it inserts a line break. */ -static int compl_enter_selects = FALSE; - -/* When "compl_leader" is not NULL only matches that start with this string - * are used. */ -static char_u *compl_leader = NULL; - -static int compl_get_longest = FALSE; /* put longest common string - in compl_leader */ - -static int compl_no_insert = FALSE; /* FALSE: select & insert - TRUE: noinsert */ -static int compl_no_select = FALSE; /* FALSE: select & insert - TRUE: noselect */ - -static int compl_used_match; /* Selected one of the matches. When - FALSE the match was edited or using - the longest common string. */ - -static int compl_was_interrupted = FALSE; /* didn't finish finding - completions. */ - -static int compl_restarting = FALSE; /* don't insert match */ - -/* When the first completion is done "compl_started" is set. When it's - * FALSE the word to be completed must be located. */ -static int compl_started = FALSE; - -/* Which Ctrl-X mode are we in? */ -static int ctrl_x_mode = CTRL_X_NORMAL; +#define BACKSPACE_CHAR 1 +#define BACKSPACE_WORD 2 +#define BACKSPACE_WORD_NOT_SPACE 3 +#define BACKSPACE_LINE 4 +#ifdef FEAT_INS_EXPAND /* Set when doing something for completion that may call edit() recursively, * which is not allowed. */ -static int compl_busy = FALSE; - -static int compl_matches = 0; -static char_u *compl_pattern = NULL; -static int compl_direction = FORWARD; -static int compl_shows_dir = FORWARD; -static int compl_pending = 0; /* > 1 for postponed CTRL-N */ -static pos_T compl_startpos; -static colnr_T compl_col = 0; /* column where the text starts - * that is being completed */ -static char_u *compl_orig_text = NULL; /* text as it was before - * completion started */ -static int compl_cont_mode = 0; -static expand_T compl_xp; - -static int compl_opt_refresh_always = FALSE; -static int compl_opt_suppress_empty = FALSE; - -static void ins_ctrl_x(void); -static int has_compl_option(int dict_opt); -static int ins_compl_accept_char(int c); -static int ins_compl_add(char_u *str, int len, int icase, char_u *fname, char_u **cptext, int cdir, int flags, int adup); -static void ins_compl_longest_match(compl_T *match); -static void ins_compl_del_pum(void); -static int pum_wanted(void); -static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir); -static char_u *find_line_end(char_u *ptr); -static void ins_compl_free(void); -static void ins_compl_clear(void); -static char_u *ins_compl_mode(void); -static int ins_compl_bs(void); -static int ins_compl_need_restart(void); -static void ins_compl_new_leader(void); -static void ins_compl_addleader(int c); -static int ins_compl_len(void); -static void ins_compl_restart(void); -static void ins_compl_set_original_text(char_u *str); -static void ins_compl_addfrommatch(void); -static int ins_compl_prep(int c); -static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg); -# if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) -static void ins_compl_add_list(list_T *list); -static void ins_compl_add_dict(dict_T *dict); -# endif -static void ins_compl_delete(void); -static void ins_compl_insert(int in_compl_func); -static int ins_compl_key2dir(int c); -static int ins_compl_pum_key(int c); -static int ins_compl_key2count(int c); -static int ins_complete(int c, int enable_pum); -static void show_pum(int prev_w_wrow, int prev_w_leftcol); -static unsigned quote_meta(char_u *dest, char_u *str, int len); +static int compl_busy = FALSE; #endif /* FEAT_INS_EXPAND */ -#define BACKSPACE_CHAR 1 -#define BACKSPACE_WORD 2 -#define BACKSPACE_WORD_NOT_SPACE 3 -#define BACKSPACE_LINE 4 -static void ins_redraw(int ready); static void ins_ctrl_v(void); #ifdef FEAT_JOB_CHANNEL static void init_prompt(int cmdchar_todo); @@ -226,12 +34,9 @@ static void insert_special(int, int, int); static void internal_format(int textwidth, int second_indent, int flags, int format_only, int c); static void check_auto_format(int); static void redo_literal(int c); -static void start_arrow(pos_T *end_insert_pos); static void start_arrow_common(pos_T *end_insert_pos, int change); #ifdef FEAT_SPELL static void check_spell_redraw(void); -static void spell_back_to_badword(void); -static int spell_bad_len = 0; /* length of located bad word */ #endif static void stop_insert(pos_T *end_insert_pos, int esc, int nomove); static int echeck_abbr(int); @@ -274,7 +79,6 @@ static void ins_pagedown(void); static void ins_drop(void); #endif static int ins_tab(void); -static int ins_eol(int c); #ifdef FEAT_DIGRAPHS static int ins_digraph(void); #endif @@ -285,7 +89,6 @@ static void ins_try_si(int c); #if defined(FEAT_EVAL) static char_u *do_insert_char_pre(int c); #endif -static int ins_apply_autocmds(event_T event); static colnr_T Insstart_textlen; /* length of line when insert started */ static colnr_T Insstart_blank_vcol; /* vcol for first inserted blank */ @@ -393,7 +196,7 @@ edit( #ifdef FEAT_INS_EXPAND /* Don't allow recursive insert mode when busy with completion. */ - if (compl_started || compl_busy || pum_visible()) + if (ins_compl_active() || compl_busy || pum_visible()) { emsg(_(e_secure)); return FALSE; @@ -834,28 +637,26 @@ edit( * and the cursor is still in the completed word. Only when there is * a match, skip this when no matches were found. */ - if (compl_started + if (ins_compl_active() && pum_wanted() - && curwin->w_cursor.col >= compl_col - && (compl_shown_match == NULL - || compl_shown_match != compl_shown_match->cp_next)) + && curwin->w_cursor.col >= ins_compl_col() + && ins_compl_has_shown_match()) { /* BS: Delete one character from "compl_leader". */ if ((c == K_BS || c == Ctrl_H) - && curwin->w_cursor.col > compl_col + && curwin->w_cursor.col > ins_compl_col() && (c = ins_compl_bs()) == NUL) continue; /* When no match was selected or it was edited. */ - if (!compl_used_match) + if (!ins_compl_used_match()) { /* CTRL-L: Add one character from the current match to * "compl_leader". Except when at the original match and * there is nothing to add, CTRL-L works like CTRL-P then. */ if (c == Ctrl_L - && (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode) - || (int)STRLEN(compl_shown_match->cp_str) - > curwin->w_cursor.col - compl_col)) + && (!ctrl_x_mode_line_or_eval() + || ins_compl_long_shown_match())) { ins_compl_addfrommatch(); continue; @@ -883,8 +684,9 @@ edit( } /* Pressing CTRL-Y selects the current match. When - * compl_enter_selects is set the Enter key does the same. */ - if ((c == Ctrl_Y || (compl_enter_selects + * ins_compl_enter_selects() is set the Enter key does the + * same. */ + if ((c == Ctrl_Y || (ins_compl_enter_selects() && (c == CAR || c == K_KENTER || c == NL))) && stop_arrow() == OK) { @@ -896,7 +698,7 @@ edit( /* Prepare for or stop CTRL-X mode. This doesn't do completion, but * it does fix up the text when finishing completion. */ - compl_get_longest = FALSE; + ins_compl_init_get_longest(); if (ins_compl_prep(c)) continue; #endif @@ -939,7 +741,7 @@ edit( #endif #ifdef FEAT_INS_EXPAND - if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) + if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode_cmdline()) goto docomplete; #endif if (c == Ctrl_V || c == Ctrl_Q) @@ -952,7 +754,7 @@ edit( #ifdef FEAT_CINDENT if (cindent_on() # ifdef FEAT_INS_EXPAND - && ctrl_x_mode == 0 + && ctrl_x_mode_none() # endif ) { @@ -1073,7 +875,7 @@ doESCkey: case Ctrl_O: /* execute one command */ #ifdef FEAT_COMPL_FUNC - if (ctrl_x_mode == CTRL_X_OMNI) + if (ctrl_x_mode_omni()) goto docomplete; #endif if (echeck_abbr(Ctrl_O + ABBR_OFF)) @@ -1149,14 +951,14 @@ doESCkey: case Ctrl_D: /* Make indent one shiftwidth smaller. */ #if defined(FEAT_INS_EXPAND) && defined(FEAT_FIND_ID) - if (ctrl_x_mode == CTRL_X_PATH_DEFINES) + if (ctrl_x_mode_path_defines()) goto docomplete; #endif /* FALLTHROUGH */ case Ctrl_T: /* Make indent one shiftwidth greater. */ # ifdef FEAT_INS_EXPAND - if (c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) + if (c == Ctrl_T && ctrl_x_mode_thesaurus()) { if (has_compl_option(FALSE)) goto docomplete; @@ -1200,7 +1002,7 @@ doESCkey: case Ctrl_U: /* delete all inserted text in current line */ # ifdef FEAT_COMPL_FUNC /* CTRL-X CTRL-U completes with 'completefunc'. */ - if (ctrl_x_mode == CTRL_X_FUNCTION) + if (ctrl_x_mode_function()) goto docomplete; # endif did_backspace = ins_bs(c, BACKSPACE_LINE, &inserted_space); @@ -1382,7 +1184,7 @@ doESCkey: case TAB: /* TAB or Complete patterns along path */ #if defined(FEAT_INS_EXPAND) && defined(FEAT_FIND_ID) - if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) + if (ctrl_x_mode_path_patterns()) goto docomplete; #endif inserted_space = FALSE; @@ -1436,7 +1238,7 @@ doESCkey: #if defined(FEAT_DIGRAPHS) || defined(FEAT_INS_EXPAND) case Ctrl_K: /* digraph or keyword completion */ # ifdef FEAT_INS_EXPAND - if (ctrl_x_mode == CTRL_X_DICTIONARY) + if (ctrl_x_mode_dictionary()) { if (has_compl_option(TRUE)) goto docomplete; @@ -1457,25 +1259,25 @@ doESCkey: break; case Ctrl_RSB: /* Tag name completion after ^X */ - if (ctrl_x_mode != CTRL_X_TAGS) + if (!ctrl_x_mode_tags()) goto normalchar; goto docomplete; case Ctrl_F: /* File name completion after ^X */ - if (ctrl_x_mode != CTRL_X_FILES) + if (!ctrl_x_mode_files()) goto normalchar; goto docomplete; case 's': /* Spelling completion after ^X */ case Ctrl_S: - if (ctrl_x_mode != CTRL_X_SPELL) + if (!ctrl_x_mode_spell()) goto normalchar; goto docomplete; #endif case Ctrl_L: /* Whole line completion after ^X */ #ifdef FEAT_INS_EXPAND - if (ctrl_x_mode != CTRL_X_WHOLE_LINE) + if (!ctrl_x_mode_whole_line()) #endif { /* CTRL-L with 'insertmode' set: Leave Insert mode */ @@ -1495,8 +1297,7 @@ doESCkey: /* if 'complete' is empty then plain ^P is no longer special, * but it is under other ^X modes */ if (*curbuf->b_p_cpt == NUL - && (ctrl_x_mode == CTRL_X_NORMAL - || ctrl_x_mode == CTRL_X_WHOLE_LINE) + && (ctrl_x_mode_normal() || ctrl_x_mode_whole_line()) && !(compl_cont_status & CONT_LOCAL)) goto normalchar; @@ -1608,7 +1409,7 @@ normalchar: if (c != K_CURSORHOLD #ifdef FEAT_COMPL_FUNC /* but not in CTRL-X mode, a script can't restore the state */ - && ctrl_x_mode == CTRL_X_NORMAL + && ctrl_x_mode_normal() #endif ) did_cursorhold = FALSE; @@ -1620,7 +1421,7 @@ normalchar: #ifdef FEAT_CINDENT if (can_cindent && cindent_on() # ifdef FEAT_INS_EXPAND - && ctrl_x_mode == CTRL_X_NORMAL + && ctrl_x_mode_normal() # endif ) { @@ -1641,6 +1442,12 @@ force_cindent: /* NOTREACHED */ } + int +ins_need_undo_get(void) +{ + return ins_need_undo; +} + /* * Redraw for Insert mode. * This is postponed until getting the next character to make '$' in the 'cpo' @@ -1648,7 +1455,7 @@ force_cindent: * Only redraw when there are no characters available. This speeds up * inserting sequences of characters (e.g., for CTRL-R). */ - static void + void ins_redraw( int ready UNUSED) /* not busy with something */ { @@ -2313,3719 +2120,6 @@ del_char_after_col(int limit_col UNUSED) return TRUE; } -#if defined(FEAT_INS_EXPAND) || defined(PROTO) -/* - * CTRL-X pressed in Insert mode. - */ - static void -ins_ctrl_x(void) -{ - /* CTRL-X after CTRL-X CTRL-V doesn't do anything, so that CTRL-X - * CTRL-V works like CTRL-N */ - if (ctrl_x_mode != CTRL_X_CMDLINE) - { - /* if the next ^X<> won't ADD nothing, then reset - * compl_cont_status */ - if (compl_cont_status & CONT_N_ADDS) - compl_cont_status |= CONT_INTRPT; - else - compl_cont_status = 0; - /* We're not sure which CTRL-X mode it will be yet */ - ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; - edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); - edit_submode_pre = NULL; - showmode(); - } -} - -/* - * Whether other than default completion has been selected. - */ - int -ctrl_x_mode_not_default(void) -{ - return ctrl_x_mode != CTRL_X_NORMAL; -} - -/* - * Whether CTRL-X was typed without a following character. - */ - int -ctrl_x_mode_not_defined_yet(void) -{ - return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET; -} - -/* - * Return TRUE if the 'dict' or 'tsr' option can be used. - */ - static int -has_compl_option(int dict_opt) -{ - if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL -# ifdef FEAT_SPELL - && !curwin->w_p_spell -# endif - ) - : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL)) - { - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - msg_attr(dict_opt ? _("'dictionary' option is empty") - : _("'thesaurus' option is empty"), - HL_ATTR(HLF_E)); - if (emsg_silent == 0) - { - vim_beep(BO_COMPL); - setcursor(); - out_flush(); -#ifdef FEAT_EVAL - if (!get_vim_var_nr(VV_TESTING)) -#endif - ui_delay(2000L, FALSE); - } - return FALSE; - } - return TRUE; -} - -/* - * Is the character 'c' a valid key to go to or keep us in CTRL-X mode? - * This depends on the current mode. - */ - int -vim_is_ctrl_x_key(int c) -{ - // Always allow ^R - let its results then be checked - if (c == Ctrl_R) - return TRUE; - - /* Accept <PageUp> and <PageDown> if the popup menu is visible. */ - if (ins_compl_pum_key(c)) - return TRUE; - - switch (ctrl_x_mode) - { - case 0: /* Not in any CTRL-X mode */ - return (c == Ctrl_N || c == Ctrl_P || c == Ctrl_X); - case CTRL_X_NOT_DEFINED_YET: - return ( c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E - || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB - || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P - || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V - || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O - || c == Ctrl_S || c == Ctrl_K || c == 's'); - case CTRL_X_SCROLL: - return (c == Ctrl_Y || c == Ctrl_E); - case CTRL_X_WHOLE_LINE: - return (c == Ctrl_L || c == Ctrl_P || c == Ctrl_N); - case CTRL_X_FILES: - return (c == Ctrl_F || c == Ctrl_P || c == Ctrl_N); - case CTRL_X_DICTIONARY: - return (c == Ctrl_K || c == Ctrl_P || c == Ctrl_N); - case CTRL_X_THESAURUS: - return (c == Ctrl_T || c == Ctrl_P || c == Ctrl_N); - case CTRL_X_TAGS: - return (c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N); -#ifdef FEAT_FIND_ID - case CTRL_X_PATH_PATTERNS: - return (c == Ctrl_P || c == Ctrl_N); - case CTRL_X_PATH_DEFINES: - return (c == Ctrl_D || c == Ctrl_P || c == Ctrl_N); -#endif - case CTRL_X_CMDLINE: - return (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N - || c == Ctrl_X); -#ifdef FEAT_COMPL_FUNC - case CTRL_X_FUNCTION: - return (c == Ctrl_U || c == Ctrl_P || c == Ctrl_N); - case CTRL_X_OMNI: - return (c == Ctrl_O || c == Ctrl_P || c == Ctrl_N); -#endif - case CTRL_X_SPELL: - return (c == Ctrl_S || c == Ctrl_P || c == Ctrl_N); - case CTRL_X_EVAL: - return (c == Ctrl_P || c == Ctrl_N); - } - internal_error("vim_is_ctrl_x_key()"); - return FALSE; -} - -/* - * Return TRUE when character "c" is part of the item currently being - * completed. Used to decide whether to abandon complete mode when the menu - * is visible. - */ - static int -ins_compl_accept_char(int c) -{ - if (ctrl_x_mode & CTRL_X_WANT_IDENT) - /* When expanding an identifier only accept identifier chars. */ - return vim_isIDc(c); - - switch (ctrl_x_mode) - { - case CTRL_X_FILES: - /* When expanding file name only accept file name chars. But not - * path separators, so that "proto/<Tab>" expands files in - * "proto", not "proto/" as a whole */ - return vim_isfilec(c) && !vim_ispathsep(c); - - case CTRL_X_CMDLINE: - case CTRL_X_OMNI: - /* Command line and Omni completion can work with just about any - * printable character, but do stop at white space. */ - return vim_isprintc(c) && !VIM_ISWHITE(c); - - case CTRL_X_WHOLE_LINE: - /* For while line completion a space can be part of the line. */ - return vim_isprintc(c); - } - return vim_iswordc(c); -} - -/* - * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the - * case of the originally typed text is used, and the case of the completed - * text is inferred, ie this tries to work out what case you probably wanted - * the rest of the word to be in -- webb - */ - int -ins_compl_add_infercase( - char_u *str, - int len, - int icase, - char_u *fname, - int dir, - int flags) -{ - char_u *p; - int i, c; - int actual_len; /* Take multi-byte characters */ - int actual_compl_length; /* into account. */ - int min_len; - int *wca; /* Wide character array. */ - int has_lower = FALSE; - int was_letter = FALSE; - - if (p_ic && curbuf->b_p_inf && len > 0) - { - /* Infer case of completed part. */ - - /* Find actual length of completion. */ - if (has_mbyte) - { - p = str; - actual_len = 0; - while (*p != NUL) - { - MB_PTR_ADV(p); - ++actual_len; - } - } - else - actual_len = len; - - /* Find actual length of original text. */ - if (has_mbyte) - { - p = compl_orig_text; - actual_compl_length = 0; - while (*p != NUL) - { - MB_PTR_ADV(p); - ++actual_compl_length; - } - } - else - actual_compl_length = compl_length; - - /* "actual_len" may be smaller than "actual_compl_length" when using - * thesaurus, only use the minimum when comparing. */ - min_len = actual_len < actual_compl_length - ? actual_len : actual_compl_length; - - /* Allocate wide character array for the completion and fill it. */ - wca = (int *)alloc((unsigned)(actual_len * sizeof(int))); - if (wca != NULL) - { - p = str; - for (i = 0; i < actual_len; ++i) - if (has_mbyte) - wca[i] = mb_ptr2char_adv(&p); - else - wca[i] = *(p++); - - /* Rule 1: Were any chars converted to lower? */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (MB_ISLOWER(c)) - { - has_lower = TRUE; - if (MB_ISUPPER(wca[i])) - { - /* Rule 1 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = MB_TOLOWER(wca[i]); - break; - } - } - } - - /* - * Rule 2: No lower case, 2nd consecutive letter converted to - * upper case. - */ - if (!has_lower) - { - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i])) - { - /* Rule 2 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = MB_TOUPPER(wca[i]); - break; - } - was_letter = MB_ISLOWER(c) || MB_ISUPPER(c); - } - } - - /* Copy the original case of the part we typed. */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (MB_ISLOWER(c)) - wca[i] = MB_TOLOWER(wca[i]); - else if (MB_ISUPPER(c)) - wca[i] = MB_TOUPPER(wca[i]); - } - - /* - * Generate encoding specific output from wide character array. - * Multi-byte characters can occupy up to five bytes more than - * ASCII characters, and we also need one byte for NUL, so stay - * six bytes away from the edge of IObuff. - */ - p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) - if (has_mbyte) - p += (*mb_char2bytes)(wca[i++], p); - else - *(p++) = wca[i++]; - *p = NUL; - - vim_free(wca); - } - - return ins_compl_add(IObuff, len, icase, fname, NULL, dir, - flags, FALSE); - } - return ins_compl_add(str, len, icase, fname, NULL, dir, flags, FALSE); -} - -/* - * Add a match to the list of matches. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error, - * maybe because alloc() returns NULL, then FAIL is returned. - */ - static int -ins_compl_add( - char_u *str, - int len, - int icase, - char_u *fname, - char_u **cptext, /* extra text for popup menu or NULL */ - int cdir, - int flags, - int adup) /* accept duplicate match */ -{ - compl_T *match; - int dir = (cdir == 0 ? compl_direction : cdir); - - ui_breakcheck(); - if (got_int) - return FAIL; - if (len < 0) - len = (int)STRLEN(str); - - /* - * If the same match is already present, don't add it. - */ - if (compl_first_match != NULL && !adup) - { - match = compl_first_match; - do - { - if ( !(match->cp_flags & ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) - return NOTDONE; - match = match->cp_next; - } while (match != NULL && match != compl_first_match); - } - - /* Remove any popup menu before changing the list of matches. */ - ins_compl_del_pum(); - - /* - * Allocate a new match structure. - * Copy the values to the new match structure. - */ - match = (compl_T *)alloc_clear((unsigned)sizeof(compl_T)); - if (match == NULL) - return FAIL; - match->cp_number = -1; - if (flags & ORIGINAL_TEXT) - match->cp_number = 0; - if ((match->cp_str = vim_strnsave(str, len)) == NULL) - { - vim_free(match); - return FAIL; - } - match->cp_icase = icase; - - /* match-fname is: - * - compl_curr_match->cp_fname if it is a string equal to fname. - * - a copy of fname, FREE_FNAME is set to free later THE allocated mem. - * - NULL otherwise. --Acevedo */ - if (fname != NULL - && compl_curr_match != NULL - && compl_curr_match->cp_fname != NULL - && STRCMP(fname, compl_curr_match->cp_fname) == 0) - match->cp_fname = compl_curr_match->cp_fname; - else if (fname != NULL) - { - match->cp_fname = vim_strsave(fname); - flags |= FREE_FNAME; - } - else - match->cp_fname = NULL; - match->cp_flags = flags; - - if (cptext != NULL) - { - int i; - - for (i = 0; i < CPT_COUNT; ++i) - if (cptext[i] != NULL && *cptext[i] != NUL) - match->cp_text[i] = vim_strsave(cptext[i]); - } - - /* - * Link the new match structure in the list of matches. - */ - if (compl_first_match == NULL) - match->cp_next = match->cp_prev = NULL; - else if (dir == FORWARD) - { - match->cp_next = compl_curr_match->cp_next; - match->cp_prev = compl_curr_match; - } - else /* BACKWARD */ - { - match->cp_next = compl_curr_match; - match->cp_prev = compl_curr_match->cp_prev; - } - if (match->cp_next) - match->cp_next->cp_prev = match; - if (match->cp_prev) - match->cp_prev->cp_next = match; - else /* if there's nothing before, it is the first match */ - compl_first_match = match; - compl_curr_match = match; - - /* - * Find the longest common string if still doing that. - */ - if (compl_get_longest && (flags & ORIGINAL_TEXT) == 0) - ins_compl_longest_match(match); - - return OK; -} - -/* - * Return TRUE if "str[len]" matches with match->cp_str, considering - * match->cp_icase. - */ - static int -ins_compl_equal(compl_T *match, char_u *str, int len) -{ - if (match->cp_icase) - return STRNICMP(match->cp_str, str, (size_t)len) == 0; - return STRNCMP(match->cp_str, str, (size_t)len) == 0; -} - -/* - * Reduce the longest common string for match "match". - */ - static void -ins_compl_longest_match(compl_T *match) -{ - char_u *p, *s; - int c1, c2; - int had_match; - - if (compl_leader == NULL) - { - /* First match, use it as a whole. */ - compl_leader = vim_strsave(match->cp_str); - if (compl_leader != NULL) - { - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) - ins_compl_delete(); - compl_used_match = FALSE; - } - } - else - { - /* Reduce the text if this match differs from compl_leader. */ - p = compl_leader; - s = match->cp_str; - while (*p != NUL) - { - if (has_mbyte) - { - c1 = mb_ptr2char(p); - c2 = mb_ptr2char(s); - } - else - { - c1 = *p; - c2 = *s; - } - if (match->cp_icase ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) - : (c1 != c2)) - break; - if (has_mbyte) - { - MB_PTR_ADV(p); - MB_PTR_ADV(s); - } - else - { - ++p; - ++s; - } - } - - if (*p != NUL) - { - /* Leader was shortened, need to change the inserted text. */ - *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) - ins_compl_delete(); - } - - compl_used_match = FALSE; - } -} - -/* - * Add an array of matches to the list of matches. - * Frees matches[]. - */ - static void -ins_compl_add_matches( - int num_matches, - char_u **matches, - int icase) -{ - int i; - int add_r = OK; - int dir = compl_direction; - - for (i = 0; i < num_matches && add_r != FAIL; i++) - if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, dir, 0, FALSE)) == OK) - /* if dir was BACKWARD then honor it just once */ - dir = FORWARD; - FreeWild(num_matches, matches); -} - -/* Make the completion list cyclic. - * Return the number of matches (excluding the original). - */ - static int -ins_compl_make_cyclic(void) -{ - compl_T *match; - int count = 0; - - if (compl_first_match != NULL) - { - /* - * Find the end of the list. - */ - match = compl_first_match; - /* there's always an entry for the compl_orig_text, it doesn't count. */ - while (match->cp_next != NULL && match->cp_next != compl_first_match) - { - match = match->cp_next; - ++count; - } - match->cp_next = compl_first_match; - compl_first_match->cp_prev = match; - } - return count; -} - -/* - * Set variables that store noselect and noinsert behavior from the - * 'completeopt' value. - */ - void -completeopt_was_set(void) -{ - compl_no_insert = FALSE; - compl_no_select = FALSE; - if (strstr((char *)p_cot, "noselect") != NULL) - compl_no_select = TRUE; - if (strstr((char *)p_cot, "noinsert") != NULL) - compl_no_insert = TRUE; -} - -/* - * Start completion for the complete() function. - * "startcol" is where the matched text starts (1 is first column). - * "list" is the list of matches. - */ - void -set_completion(colnr_T startcol, list_T *list) -{ - int save_w_wrow = curwin->w_wrow; - int save_w_leftcol = curwin->w_leftcol; - - /* If already doing completions stop it. */ - if (ctrl_x_mode != CTRL_X_NORMAL) - ins_compl_prep(' '); - ins_compl_clear(); - ins_compl_free(); - - compl_direction = FORWARD; - if (startcol > curwin->w_cursor.col) - startcol = curwin->w_cursor.col; - compl_col = startcol; - compl_length = (int)curwin->w_cursor.col - (int)startcol; - /* compl_pattern doesn't need to be set */ - compl_orig_text = vim_strnsave(ml_get_curline() + compl_col, compl_length); - if (compl_orig_text == NULL || ins_compl_add(compl_orig_text, - -1, p_ic, NULL, NULL, 0, ORIGINAL_TEXT, FALSE) != OK) - return; - - ctrl_x_mode = CTRL_X_EVAL; - - ins_compl_add_list(list); - compl_matches = ins_compl_make_cyclic(); - compl_started = TRUE; - compl_used_match = TRUE; - compl_cont_status = 0; - - compl_curr_match = compl_first_match; - if (compl_no_insert || compl_no_select) - { - ins_complete(K_DOWN, FALSE); - if (compl_no_select) - /* Down/Up has no real effect. */ - ins_complete(K_UP, FALSE); - } - else - ins_complete(Ctrl_N, FALSE); - compl_enter_selects = compl_no_insert; - - /* Lazily show the popup menu, unless we got interrupted. */ - if (!compl_interrupted) - show_pum(save_w_wrow, save_w_leftcol); - out_flush(); -} - - -/* "compl_match_array" points the currently displayed list of entries in the - * popup menu. It is NULL when there is no popup menu. */ -static pumitem_T *compl_match_array = NULL; -static int compl_match_arraysize; - -/* - * Update the screen and when there is any scrolling remove the popup menu. - */ - static void -ins_compl_upd_pum(void) -{ - int h; - - if (compl_match_array != NULL) - { - h = curwin->w_cline_height; - // Update the screen later, before drawing the popup menu over it. - pum_call_update_screen(); - if (h != curwin->w_cline_height) - ins_compl_del_pum(); - } -} - -/* - * Remove any popup menu. - */ - static void -ins_compl_del_pum(void) -{ - if (compl_match_array != NULL) - { - pum_undisplay(); - VIM_CLEAR(compl_match_array); - } -} - -/* - * Return TRUE if the popup menu should be displayed. - */ - static int -pum_wanted(void) -{ - /* 'completeopt' must contain "menu" or "menuone" */ - if (vim_strchr(p_cot, 'm') == NULL) - return FALSE; - - /* The display looks bad on a B&W display. */ - if (t_colors < 8 -#ifdef FEAT_GUI - && !gui.in_use -#endif - ) - return FALSE; - return TRUE; -} |