diff options
Diffstat (limited to 'data/vim/patches/8.1.1076')
-rw-r--r-- | data/vim/patches/8.1.1076 | 9225 |
1 files changed, 0 insertions, 9225 deletions
diff --git a/data/vim/patches/8.1.1076 b/data/vim/patches/8.1.1076 deleted file mode 100644 index 4e18d4463..000000000 --- a/data/vim/patches/8.1.1076 +++ /dev/null @@ -1,9225 +0,0 @@ -To: vim_dev@googlegroups.com -Subject: Patch 8.1.1076 -Fcc: outbox -From: Bram Moolenaar <Bram@moolenaar.net> -Mime-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit ------------- - -Patch 8.1.1076 -Problem: File for Insert mode is much too big. -Solution: Split off the code for Insert completion. (Yegappan Lakshmanan, - closes #4044) -Files: Filelist, src/Make_bc5.mak, src/Make_cyg_ming.mak, - src/Make_dice.mak, src/Make_ivc.mak, src/Make_manx.mak, - src/Make_morph.mak, src/Make_mvc.mak, src/Make_sas.mak, - src/Make_vms.mms, src/Makefile, src/edit.c, src/evalfunc.c, - src/globals.h, src/insexpand.c, src/misc2.c, src/proto.h, - src/proto/edit.pro, src/proto/insexpand.pro, src/search.c, - src/spell.c, src/structs.h, src/tag.c - - -*** ../vim-8.1.1075/Filelist 2019-03-29 13:09:37.972782868 +0100 ---- Filelist 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 48,53 **** ---- 48,54 ---- - src/hardcopy.c \ - src/hashtab.c \ - src/indent.c \ -+ src/insexpand.c \ - src/json.c \ - src/json_test.c \ - src/kword_test.c \ -*************** -*** 175,180 **** ---- 176,182 ---- - src/proto/hardcopy.pro \ - src/proto/hashtab.pro \ - src/proto/indent.pro \ -+ src/proto/insexpand.pro \ - src/proto/json.pro \ - src/proto/list.pro \ - src/proto/main.pro \ -*** ../vim-8.1.1075/src/Make_bc5.mak 2019-03-28 22:43:12.103997449 +0100 ---- src/Make_bc5.mak 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 534,539 **** ---- 534,540 ---- - $(OBJDIR)\hardcopy.obj \ - $(OBJDIR)\hashtab.obj \ - $(OBJDIR)\indent.obj \ -+ $(OBJDIR)\insexpand.obj \ - $(OBJDIR)\json.obj \ - $(OBJDIR)\list.obj \ - $(OBJDIR)\main.obj \ -*** ../vim-8.1.1075/src/Make_cyg_ming.mak 2019-03-22 16:57:42.061691287 +0100 ---- src/Make_cyg_ming.mak 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 721,726 **** ---- 721,727 ---- - $(OUTDIR)/hardcopy.o \ - $(OUTDIR)/hashtab.o \ - $(OUTDIR)/indent.o \ -+ $(OUTDIR)/insexpand.o \ - $(OUTDIR)/json.o \ - $(OUTDIR)/list.o \ - $(OUTDIR)/main.o \ -*** ../vim-8.1.1075/src/Make_dice.mak 2019-02-16 15:09:21.221946179 +0100 ---- src/Make_dice.mak 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 51,56 **** ---- 51,57 ---- - hardcopy.c \ - hashtab.c \ - indent.c \ -+ insexpand.c \ - json.c \ - list.c \ - main.c \ -*************** -*** 110,115 **** ---- 111,117 ---- - o/hardcopy.o \ - o/hashtab.o \ - o/indent.o \ -+ o/insexpand.o \ - o/json.o \ - o/list.o \ - o/main.o \ -*************** -*** 213,218 **** ---- 215,222 ---- - - o/indent.o: indent.c $(SYMS) - -+ o/insexpand.o: insexpand.c $(SYMS) -+ - o/json.o: json.c $(SYMS) - - o/list.o: list.c $(SYMS) -*** ../vim-8.1.1075/src/Make_ivc.mak 2019-02-18 21:41:34.477750367 +0100 ---- src/Make_ivc.mak 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 235,240 **** ---- 235,241 ---- - "$(INTDIR)/hardcopy.obj" \ - "$(INTDIR)/hashtab.obj" \ - "$(INTDIR)/indent.obj" \ -+ "$(INTDIR)/insexpand.obj" \ - "$(INTDIR)/json.obj" \ - "$(INTDIR)/list.obj" \ - "$(INTDIR)/main.obj" \ -*************** -*** 439,444 **** ---- 440,449 ---- - SOURCE=.\indent.c - # End Source File - # Begin Source File -+ # -+ SOURCE=.\insexpand.c -+ # End Source File -+ # Begin Source File - - SOURCE=.\gui.c - -*** ../vim-8.1.1075/src/Make_manx.mak 2019-02-16 15:09:21.221946179 +0100 ---- src/Make_manx.mak 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 61,66 **** ---- 61,67 ---- - hardcopy.c \ - hashtab.c \ - indent.c \ -+ insexpand.c \ - json.c \ - list.c \ - main.c \ -*************** -*** 122,127 **** ---- 123,129 ---- - obj/hardcopy.o \ - obj/hashtab.o \ - obj/indent.o \ -+ obj/insexpand.o \ - obj/json.o \ - obj/list.o \ - obj/main.o \ -*************** -*** 181,186 **** ---- 183,189 ---- - proto/hardcopy.pro \ - proto/hashtab.pro \ - proto/indent.pro \ -+ proto/insexpand.pro \ - proto/json.pro \ - proto/list.pro \ - proto/main.pro \ -*************** -*** 335,340 **** ---- 338,346 ---- - obj/indent.o: indent.c - $(CCSYM) $@ indent.c - -+ obj/insexpand.o: insexpand.c -+ $(CCSYM) $@ insexpand.c -+ - obj/json.o: json.c - $(CCSYM) $@ json.c - -*** ../vim-8.1.1075/src/Make_morph.mak 2019-02-16 15:09:21.221946179 +0100 ---- src/Make_morph.mak 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 49,54 **** ---- 49,55 ---- - hardcopy.c \ - hashtab.c \ - indent.c \ -+ insexpand.c \ - json.c \ - list.c \ - main.c \ -*** ../vim-8.1.1075/src/Make_mvc.mak 2019-03-22 16:57:42.061691287 +0100 ---- src/Make_mvc.mak 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 725,730 **** ---- 725,731 ---- - $(OUTDIR)\hardcopy.obj \ - $(OUTDIR)\hashtab.obj \ - $(OUTDIR)\indent.obj \ -+ $(OUTDIR)\insexpand.obj \ - $(OUTDIR)\json.obj \ - $(OUTDIR)\list.obj \ - $(OUTDIR)\main.obj \ -*************** -*** 1416,1421 **** ---- 1417,1424 ---- - - $(OUTDIR)/indent.obj: $(OUTDIR) indent.c $(INCL) - -+ $(OUTDIR)/insexpand.obj: $(OUTDIR) insexpand.c $(INCL) -+ - $(OUTDIR)/gui.obj: $(OUTDIR) gui.c $(INCL) $(GUI_INCL) - - $(OUTDIR)/gui_beval.obj: $(OUTDIR) gui_beval.c $(INCL) $(GUI_INCL) -*************** -*** 1648,1653 **** ---- 1651,1657 ---- - proto/hardcopy.pro \ - proto/hashtab.pro \ - proto/indent.pro \ -+ proto/insexpand.pro \ - proto/json.pro \ - proto/list.pro \ - proto/main.pro \ -*** ../vim-8.1.1075/src/Make_sas.mak 2019-02-16 15:09:21.221946179 +0100 ---- src/Make_sas.mak 2019-03-30 12:54:54.297407936 +0100 -*************** -*** 114,119 **** ---- 114,120 ---- - hardcopy.c \ - hashtab.c \ - indent.c \ -+ insexpand.c \ - json.c \ - list.c \ - main.c \ -*************** -*** 174,179 **** ---- 175,181 ---- - hardcopy.o \ - hashtab.o \ - indent.o \ -+ insexpand.o \ - json.o \ - list.o \ - main.o \ -*************** -*** 234,239 **** ---- 236,242 ---- - proto/hardcopy.pro \ - proto/hashtab.pro \ - proto/indent.pro \ -+ proto/insexpand.pro \ - proto/json.pro \ - proto/list.pro \ - proto/main.pro \ -*************** -*** 373,378 **** ---- 376,383 ---- - proto/hashtab.pro: hashtab.c - indent.o: indent.c - proto/indent.pro: indent.c -+ insexpand.o: insexpand.c -+ proto/insexpand.pro: insexpand.c - json.o: json.c - proto/json.pro: json.c - list.o: list.c -*** ../vim-8.1.1075/src/Make_vms.mms 2019-03-22 17:03:01.783689365 +0100 ---- src/Make_vms.mms 2019-03-30 12:54:54.301407905 +0100 -*************** -*** 311,319 **** - crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c evalfunc.c \ - ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c if_cscope.c \ - if_xcmdsrv.c fileio.c findfile.c fold.c getchar.c hardcopy.c \ -! hashtab.c indent.c json.c list.c main.c mark.c menu.c mbyte.c \ -! memfile.c memline.c message.c misc1.c misc2.c move.c normal.c ops.c \ -! option.c popupmnu.c quickfix.c regexp.c search.c sha256.c sign.c \ - spell.c spellfile.c syntax.c tag.c term.c termlib.c textprop.c ui.c \ - undo.c userfunc.c version.c screen.c window.c os_unix.c os_vms.c \ - pathdef.c ---- 311,319 ---- - crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c evalfunc.c \ - ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c if_cscope.c \ - if_xcmdsrv.c fileio.c findfile.c fold.c getchar.c hardcopy.c \ -! hashtab.c indent.c insexpand.c json.c list.c main.c mark.c menu.c \ -! mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c normal.c \ -! ops.c option.c popupmnu.c quickfix.c regexp.c search.c sha256.c sign.c \ - spell.c spellfile.c syntax.c tag.c term.c termlib.c textprop.c ui.c \ - undo.c userfunc.c version.c screen.c window.c os_unix.c os_vms.c \ - pathdef.c -*************** -*** 325,337 **** - edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj \ - ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ - fileio.obj findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ -! indent.obj json.obj list.obj main.obj mark.obj menu.obj memfile.obj \ -! memline.obj message.obj misc1.obj misc2.obj move.obj mbyte.obj \ -! normal.obj ops.obj option.obj popupmnu.obj quickfix.obj regexp.obj \ -! search.obj sha256.obj sign.obj spell.obj spellfile.obj syntax.obj \ -! tag.obj term.obj termlib.obj textprop.obj ui.obj undo.obj \ -! userfunc.obj screen.obj version.obj window.obj os_unix.obj os_vms.obj \ -! pathdef.obj if_mzsch.obj \ - $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ - $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) - ---- 325,337 ---- - edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj \ - ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ - fileio.obj findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ -! indent.obj insexpand.obj json.obj list.obj main.obj mark.obj \ -! menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \ -! move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \ -! quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \ -! spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \ -! ui.obj undo.obj userfunc.obj screen.obj version.obj window.obj \ -! os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ - $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ - $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) - -*************** -*** 603,608 **** ---- 603,609 ---- - regexp.h gui.h beval.h [.proto]gui_beval.pro ex_cmds.h proto.h \ - globals.h if_mzsch.h - indent.obj : indent.c vim.h [.auto]config.h feature.h os_unix.h -+ insexpand.obj : insexpand.c vim.h [.auto]config.h feature.h os_unix.h - json.obj : json.c vim.h [.auto]config.h feature.h os_unix.h \ - ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ -*** ../vim-8.1.1075/src/Makefile 2019-03-22 16:33:03.487016094 +0100 ---- src/Makefile 2019-03-30 12:54:54.301407905 +0100 -*************** -*** 1599,1604 **** ---- 1599,1605 ---- - if_cscope.c \ - if_xcmdsrv.c \ - indent.c \ -+ insexpand.c \ - json.c \ - list.c \ - main.c \ -*************** -*** 1713,1718 **** ---- 1714,1720 ---- - objects/if_cscope.o \ - objects/if_xcmdsrv.o \ - objects/indent.o \ -+ objects/insexpand.o \ - objects/list.o \ - objects/mark.o \ - objects/memline.o \ -*************** -*** 1844,1849 **** ---- 1846,1852 ---- - if_ruby.pro \ - if_xcmdsrv.pro \ - indent.pro \ -+ insexpand.pro \ - json.pro \ - list.pro \ - main.pro \ -*************** -*** 3098,3103 **** ---- 3101,3109 ---- - objects/indent.o: indent.c - $(CCC) -o $@ indent.c - -+ objects/insexpand.o: insexpand.c -+ $(CCC) -o $@ insexpand.c -+ - objects/json.o: json.c - $(CCC) -o $@ json.c - -*************** -*** 3500,3505 **** ---- 3506,3515 ---- - auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ - proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ - proto.h globals.h -+ objects/insexpand.o: insexpand.c vim.h protodef.h auto/config.h feature.h \ -+ os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ -+ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ -+ proto.h globals.h - objects/json.o: json.c vim.h protodef.h auto/config.h feature.h os_unix.h \ - auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ - proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ -*** ../vim-8.1.1075/src/edit.c 2019-03-29 12:19:34.953348924 +0100 ---- src/edit.c 2019-03-30 13:46:18.517111390 +0100 -*************** -*** 13,222 **** - - #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; - - /* 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); - #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); ---- 13,30 ---- - - #include "vim.h" - -! #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; - #endif /* FEAT_INS_EXPAND */ - - - static void ins_ctrl_v(void); - #ifdef FEAT_JOB_CHANNEL - static void init_prompt(int cmdchar_todo); -*************** -*** 226,237 **** - 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); ---- 34,42 ---- -*************** -*** 274,280 **** - 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 ---- 79,84 ---- -*************** -*** 285,291 **** - #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 */ ---- 89,94 ---- -*************** -*** 393,399 **** - - #ifdef FEAT_INS_EXPAND - /* Don't allow recursive insert mode when busy with completion. */ -! if (compl_started || compl_busy || pum_visible()) - { - emsg(_(e_secure)); - return FALSE; ---- 196,202 ---- - - #ifdef FEAT_INS_EXPAND - /* Don't allow recursive insert mode when busy with completion. */ -! if (ins_compl_active() || compl_busy || pum_visible()) - { - emsg(_(e_secure)); - return FALSE; -*************** -*** 834,861 **** - * 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 - && pum_wanted() -! && curwin->w_cursor.col >= compl_col -! && (compl_shown_match == NULL -! || compl_shown_match != compl_shown_match->cp_next)) - { - /* BS: Delete one character from "compl_leader". */ - if ((c == K_BS || c == Ctrl_H) -! && curwin->w_cursor.col > compl_col - && (c = ins_compl_bs()) == NUL) - continue; - - /* When no match was selected or it was edited. */ -! if (!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)) - { - ins_compl_addfrommatch(); - continue; ---- 637,662 ---- - * and the cursor is still in the completed word. Only when there is - * a match, skip this when no matches were found. - */ -! if (ins_compl_active() - && pum_wanted() -! && 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 > ins_compl_col() - && (c = ins_compl_bs()) == NUL) - continue; - - /* When no match was selected or it was edited. */ -! 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() -! || ins_compl_long_shown_match())) - { - ins_compl_addfrommatch(); - continue; -*************** -*** 883,890 **** - } - - /* 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 - && (c == CAR || c == K_KENTER || c == NL))) - && stop_arrow() == OK) - { ---- 684,692 ---- - } - - /* Pressing CTRL-Y selects the current match. When -! * 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,902 **** - - /* 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; - if (ins_compl_prep(c)) - continue; - #endif ---- 698,704 ---- - - /* Prepare for or stop CTRL-X mode. This doesn't do completion, but - * it does fix up the text when finishing completion. */ -! ins_compl_init_get_longest(); - if (ins_compl_prep(c)) - continue; - #endif -*************** -*** 939,945 **** - #endif - - #ifdef FEAT_INS_EXPAND -! if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) - goto docomplete; - #endif - if (c == Ctrl_V || c == Ctrl_Q) ---- 741,747 ---- - #endif - - #ifdef FEAT_INS_EXPAND -! if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode_cmdline()) - goto docomplete; - #endif - if (c == Ctrl_V || c == Ctrl_Q) -*************** -*** 952,958 **** - #ifdef FEAT_CINDENT - if (cindent_on() - # ifdef FEAT_INS_EXPAND -! && ctrl_x_mode == 0 - # endif - ) - { ---- 754,760 ---- - #ifdef FEAT_CINDENT - if (cindent_on() - # ifdef FEAT_INS_EXPAND -! && ctrl_x_mode_none() - # endif - ) - { -*************** -*** 1073,1079 **** - - case Ctrl_O: /* execute one command */ - #ifdef FEAT_COMPL_FUNC -! if (ctrl_x_mode == CTRL_X_OMNI) - goto docomplete; - #endif - if (echeck_abbr(Ctrl_O + ABBR_OFF)) ---- 875,881 ---- - - case Ctrl_O: /* execute one command */ - #ifdef FEAT_COMPL_FUNC -! if (ctrl_x_mode_omni()) - goto docomplete; - #endif - if (echeck_abbr(Ctrl_O + ABBR_OFF)) -*************** -*** 1149,1162 **** - - 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) - 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 (has_compl_option(FALSE)) - goto docomplete; ---- 951,964 ---- - - case Ctrl_D: /* Make indent one shiftwidth smaller. */ - #if defined(FEAT_INS_EXPAND) && defined(FEAT_FIND_ID) -! 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_thesaurus()) - { - if (has_compl_option(FALSE)) - goto docomplete; -*************** -*** 1200,1206 **** - 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) - goto docomplete; - # endif - did_backspace = ins_bs(c, BACKSPACE_LINE, &inserted_space); ---- 1002,1008 ---- - 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_function()) - goto docomplete; - # endif - did_backspace = ins_bs(c, BACKSPACE_LINE, &inserted_space); -*************** -*** 1382,1388 **** - - 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) - goto docomplete; - #endif - inserted_space = FALSE; ---- 1184,1190 ---- - - case TAB: /* TAB or Complete patterns along path */ - #if defined(FEAT_INS_EXPAND) && defined(FEAT_FIND_ID) -! if (ctrl_x_mode_path_patterns()) - goto docomplete; - #endif - inserted_space = FALSE; -*************** -*** 1436,1442 **** - #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 (has_compl_option(TRUE)) - goto docomplete; ---- 1238,1244 ---- - #if defined(FEAT_DIGRAPHS) || defined(FEAT_INS_EXPAND) - case Ctrl_K: /* digraph or keyword completion */ - # ifdef FEAT_INS_EXPAND -! if (ctrl_x_mode_dictionary()) - { - if (has_compl_option(TRUE)) - goto docomplete; -*************** -*** 1457,1481 **** - break; - - case Ctrl_RSB: /* Tag name completion after ^X */ -! if (ctrl_x_mode != CTRL_X_TAGS) - goto normalchar; - goto docomplete; - - case Ctrl_F: /* File name completion after ^X */ -! if (ctrl_x_mode != CTRL_X_FILES) - goto normalchar; - goto docomplete; - - case 's': /* Spelling completion after ^X */ - case Ctrl_S: -! if (ctrl_x_mode != CTRL_X_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) - #endif - { - /* CTRL-L with 'insertmode' set: Leave Insert mode */ ---- 1259,1283 ---- - break; - - case Ctrl_RSB: /* Tag name completion after ^X */ -! if (!ctrl_x_mode_tags()) - goto normalchar; - goto docomplete; - - case Ctrl_F: /* File name completion after ^X */ -! if (!ctrl_x_mode_files()) - goto normalchar; - goto docomplete; - - case 's': /* Spelling completion after ^X */ - case Ctrl_S: -! 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_whole_line()) - #endif - { - /* CTRL-L with 'insertmode' set: Leave Insert mode */ -*************** -*** 1495,1502 **** - /* 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) - && !(compl_cont_status & CONT_LOCAL)) - goto normalchar; - ---- 1297,1303 ---- - /* 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_normal() || ctrl_x_mode_whole_line()) - && !(compl_cont_status & CONT_LOCAL)) - goto normalchar; - -*************** -*** 1608,1614 **** - 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 - #endif - ) - did_cursorhold = FALSE; ---- 1409,1415 ---- - if (c != K_CURSORHOLD - #ifdef FEAT_COMPL_FUNC - /* but not in CTRL-X mode, a script can't restore the state */ -! && ctrl_x_mode_normal() - #endif - ) - did_cursorhold = FALSE; -*************** -*** 1620,1626 **** - #ifdef FEAT_CINDENT - if (can_cindent && cindent_on() - # ifdef FEAT_INS_EXPAND -! && ctrl_x_mode == CTRL_X_NORMAL - # endif - ) - { ---- 1421,1427 ---- - #ifdef FEAT_CINDENT - if (can_cindent && cindent_on() - # ifdef FEAT_INS_EXPAND -! && ctrl_x_mode_normal() - # endif - ) - { -*************** -*** 1641,1646 **** ---- 1442,1453 ---- - /* 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,1654 **** - * Only redraw when there are no characters available. This speeds up - * inserting sequences of characters (e.g., for CTRL-R). - */ -! static void - ins_redraw( - int ready UNUSED) /* not busy with something */ - { ---- 1455,1461 ---- - * Only redraw when there are no characters available. This speeds up - * inserting sequences of characters (e.g., for CTRL-R). - */ -! void - ins_redraw( - int ready UNUSED) /* not busy with something */ - { -*************** -*** 2313,6031 **** - 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; -- } -- -- /* -- * Return TRUE if there are two or more matches to be shown in the popup menu. -- * One if 'completopt' contains "menuone". -- */ -- static int -- pum_enough_matches(void) -- { -- compl_T *compl; -- int i; -- -- /* Don't display the popup menu if there are no matches or there is only -- * one (ignoring the original text). */ -- compl = compl_first_match; -- i = 0; -- do -- { -- if (compl == NULL -- || ((compl->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) -- break; -- compl = compl->cp_next; -- } while (compl != compl_first_match); -- -- if (strstr((char *)p_cot, "menuone") != NULL) -- return (i >= 1); -- return (i >= 2); -- } -- -- /* -- * Show the popup menu for the list of matches. -- * Also adjusts "compl_shown_match" to an entry that is actually displayed. -- */ -- void -- ins_compl_show_pum(void) -- { -- compl_T *compl; -- compl_T *shown_compl = NULL; -- int did_find_shown_match = FALSE; -- int shown_match_ok = FALSE; -- int i; -- int cur = -1; -- colnr_T col; -- int lead_len = 0; -- -- if (!pum_wanted() || !pum_enough_matches()) -- return; -- -- #if defined(FEAT_EVAL) -- /* Dirty hard-coded hack: remove any matchparen highlighting. */ -- do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|3match none|endif"); -- #endif -- -- // Update the screen later, before drawing the popup menu over it. -- pum_call_update_screen(); -- -- if (compl_match_array == NULL) -- { -- /* Need to build the popup menu list. */ -- compl_match_arraysize = 0; -- compl = compl_first_match; -- if (compl_leader != NULL) -- lead_len = (int)STRLEN(compl_leader); -- do -- { -- if ((compl->cp_flags & ORIGINAL_TEXT) == 0 -- && (compl_leader == NULL -- || ins_compl_equal(compl, compl_leader, lead_len))) -- ++compl_match_arraysize; -- compl = compl->cp_next; -- } while (compl != NULL && compl != compl_first_match); -- if (compl_match_arraysize == 0) -- return; -- compl_match_array = (pumitem_T *)alloc_clear( -- (unsigned)(sizeof(pumitem_T) -- * compl_match_arraysize)); -- if (compl_match_array != NULL) -- { -- /* If the current match is the original text don't find the first -- * match after it, don't highlight anything. */ -- if (compl_shown_match->cp_flags & ORIGINAL_TEXT) -- shown_match_ok = TRUE; -- -- i = 0; -- compl = compl_first_match; -- do -- { -- if ((compl->cp_flags & ORIGINAL_TEXT) == 0 -- && (compl_leader == NULL -- || ins_compl_equal(compl, compl_leader, lead_len))) -- { -- if (!shown_match_ok) -- { -- if (compl == compl_shown_match || did_find_shown_match) -- { -- /* This item is the shown match or this is the -- * first displayed item after the shown match. */ -- compl_shown_match = compl; -- did_find_shown_match = TRUE; -- shown_match_ok = TRUE; -- } -- else -- /* Remember this displayed match for when the -- * shown match is just below it. */ -- shown_compl = compl; -- cur = i; -- } -- -- if (compl->cp_text[CPT_ABBR] != NULL) -- compl_match_array[i].pum_text = -- compl->cp_text[CPT_ABBR]; -- else -- compl_match_array[i].pum_text = compl->cp_str; -- compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; -- compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; -- if (compl->cp_text[CPT_MENU] != NULL) -- compl_match_array[i++].pum_extra = -- compl->cp_text[CPT_MENU]; -- else -- compl_match_array[i++].pum_extra = compl->cp_fname; -- } -- -- if (compl == compl_shown_match) -- { -- did_find_shown_match = TRUE; -- -- /* When the original text is the shown match don't set -- * compl_shown_match. */ -- if (compl->cp_flags & ORIGINAL_TEXT) -- shown_match_ok = TRUE; -- -- if (!shown_match_ok && shown_compl != NULL) -- { -- /* The shown match isn't displayed, set it to the -- * previously displayed match. */ -- compl_shown_match = shown_compl; -- shown_match_ok = TRUE; -- } -- } -- compl = compl->cp_next; -- } while (compl != NULL && compl != compl_first_match); -- -- if (!shown_match_ok) /* no displayed match at all */ -- cur = -1; -- } -- } -- else -- { -- /* popup menu already exists, only need to find the current item.*/ -- for (i = 0; i < compl_match_arraysize; ++i) -- if (compl_match_array[i].pum_text == compl_shown_match->cp_str -- || compl_match_array[i].pum_text -- == compl_shown_match->cp_text[CPT_ABBR]) -- { -- cur = i; -- break; -- } -- } -- -- if (compl_match_array != NULL) -- { -- /* In Replace mode when a $ is displayed at the end of the line only -- * part of the screen would be updated. We do need to redraw here. */ -- dollar_vcol = -1; -- -- /* Compute the screen column of the start of the completed text. -- * Use the cursor to get all wrapping and other settings right. */ -- col = curwin->w_cursor.col; -- curwin->w_cursor.col = compl_col; -- pum_display(compl_match_array, compl_match_arraysize, cur); -- curwin->w_cursor.col = col; -- } -- } -- -- #define DICT_FIRST (1) /* use just first element in "dict" */ -- #define DICT_EXACT (2) /* "dict" is the exact name of a file */ -- -- /* -- * Add any identifiers that match the given pattern in the list of dictionary -- * files "dict_start" to the list of completions. -- */ -- static void -- ins_compl_dictionaries( -- char_u *dict_start, -- char_u *pat, -- int flags, /* DICT_FIRST and/or DICT_EXACT */ -- int thesaurus) /* Thesaurus completion */ -- { -- char_u *dict = dict_start; -- char_u *ptr; -- char_u *buf; -- regmatch_T regmatch; -- char_u **files; -- int count; -- int save_p_scs; -- int dir = compl_direction; -- -- if (*dict == NUL) -- { -- #ifdef FEAT_SPELL -- /* When 'dictionary' is empty and spell checking is enabled use -- * "spell". */ -- if (!thesaurus && curwin->w_p_spell) -- dict = (char_u *)"spell"; -- else -- #endif -- return; -- } -- -- buf = alloc(LSIZE); -- if (buf == NULL) -- return; -- regmatch.regprog = NULL; /* so that we can goto theend */ -- -- /* If 'infercase' is set, don't use 'smartcase' here */ -- save_p_scs = p_scs; -- if (curbuf->b_p_inf) -- p_scs = FALSE; -- -- /* When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern -- * to only match at the start of a line. Otherwise just match the -- * pattern. Also need to double backslashes. */ -- if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- { -- char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); -- size_t len; -- -- if (pat_esc == NULL) -- goto theend; -- len = STRLEN(pat_esc) + 10; -- ptr = alloc((unsigned)len); -- if (ptr == NULL) -- { -- vim_free(pat_esc); -- goto theend; -- } -- vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); -- regmatch.regprog = vim_regcomp(ptr, RE_MAGIC); -- vim_free(pat_esc); -- vim_free(ptr); -- } -- else -- { -- regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); -- if (regmatch.regprog == NULL) -- goto theend; -- } -- -- /* ignore case depends on 'ignorecase', 'smartcase' and "pat" */ -- regmatch.rm_ic = ignorecase(pat); -- while (*dict != NUL && !got_int && !compl_interrupted) -- { -- /* copy one dictionary file name into buf */ -- if (flags == DICT_EXACT) -- { -- count = 1; -- files = &dict; -- } -- else -- { -- /* Expand wildcards in the dictionary name, but do not allow -- * backticks (for security, the 'dict' option may have been set in -- * a modeline). */ -- copy_option_part(&dict, buf, LSIZE, ","); -- # ifdef FEAT_SPELL -- if (!thesaurus && STRCMP(buf, "spell") == 0) -- count = -1; -- else -- # endif -- if (vim_strchr(buf, '`') != NULL -- || expand_wildcards(1, &buf, &count, &files, -- EW_FILE|EW_SILENT) != OK) -- count = 0; -- } -- -- # ifdef FEAT_SPELL -- if (count == -1) -- { -- /* Complete from active spelling. Skip "\<" in the pattern, we -- * don't use it as a RE. */ -- if (pat[0] == '\\' && pat[1] == '<') -- ptr = pat + 2; -- else -- ptr = pat; -- spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); -- } -- else -- # endif -- if (count > 0) /* avoid warning for using "files" uninit */ -- { -- ins_compl_files(count, files, thesaurus, flags, -- ®match, buf, &dir); -- if (flags != DICT_EXACT) -- FreeWild(count, files); -- } -- if (flags != 0) -- break; -- } -- -- theend: -- p_scs = save_p_scs; -- vim_regfree(regmatch.regprog); -- vim_free(buf); -- } -- -- static void -- ins_compl_files( -- int count, -- char_u **files, -- int thesaurus, -- int flags, -- regmatch_T *regmatch, -- char_u *buf, -- int *dir) -- { -- char_u *ptr; -- int i; -- FILE *fp; -- int add_r; -- -- for (i = 0; i < count && !got_int && !compl_interrupted; i++) -- { -- fp = mch_fopen((char *)files[i], "r"); /* open dictionary file */ -- if (flags != DICT_EXACT) -- { -- vim_snprintf((char *)IObuff, IOSIZE, -- _("Scanning dictionary: %s"), (char *)files[i]); -- (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); -- } -- -- if (fp != NULL) -- { -- /* -- * Read dictionary file line by line. -- * Check each line for a match. -- */ -- while (!got_int && !compl_interrupted -- && !vim_fgets(buf, LSIZE, fp)) -- { -- ptr = buf; -- while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) -- { -- ptr = regmatch->startp[0]; -- if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- ptr = find_line_end(ptr); -- else -- ptr = find_word_end(ptr); -- add_r = ins_compl_add_infercase(regmatch->startp[0], -- (int)(ptr - regmatch->startp[0]), -- p_ic, files[i], *dir, 0); -- if (thesaurus) -- { -- char_u *wstart; -- -- /* -- * Add the other matches on the line -- */ -- ptr = buf; -- while (!got_int) -- { -- /* Find start of the next word. Skip white -- * space and punctuation. */ -- ptr = find_word_start(ptr); -- if (*ptr == NUL || *ptr == NL) -- break; -- wstart = ptr; -- -- /* Find end of the word. */ -- if (has_mbyte) -- /* Japanese words may have characters in -- * different classes, only separate words -- * with single-byte non-word characters. */ -- while (*ptr != NUL) -- { -- int l = (*mb_ptr2len)(ptr); -- -- if (l < 2 && !vim_iswordc(*ptr)) -- break; -- ptr += l; -- } -- else -- ptr = find_word_end(ptr); -- -- /* Add the word. Skip the regexp match. */ -- if (wstart != regmatch->startp[0]) -- add_r = ins_compl_add_infercase(wstart, -- (int)(ptr - wstart), -- p_ic, files[i], *dir, 0); -- } -- } -- if (add_r == OK) -- /* if dir was BACKWARD then honor it just once */ -- *dir = FORWARD; -- else if (add_r == FAIL) -- break; -- /* avoid expensive call to vim_regexec() when at end -- * of line */ -- if (*ptr == '\n' || got_int) -- break; -- } -- line_breakcheck(); -- ins_compl_check_keys(50, FALSE); -- } -- fclose(fp); -- } -- } -- } -- -- /* -- * Find the start of the next word. -- * Returns a pointer to the first char of the word. Also stops at a NUL. -- */ -- char_u * -- find_word_start(char_u *ptr) -- { -- if (has_mbyte) -- while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) -- ptr += (*mb_ptr2len)(ptr); -- else -- while (*ptr != NUL && *ptr != '\n' && !vim_iswordc(*ptr)) -- ++ptr; -- return ptr; -- } -- -- /* -- * Find the end of the word. Assumes it starts inside a word. -- * Returns a pointer to just after the word. -- */ -- char_u * -- find_word_end(char_u *ptr) -- { -- int start_class; -- -- if (has_mbyte) -- { -- start_class = mb_get_class(ptr); -- if (start_class > 1) -- while (*ptr != NUL) -- { -- ptr += (*mb_ptr2len)(ptr); -- if (mb_get_class(ptr) != start_class) -- break; -- } -- } -- else -- while (vim_iswordc(*ptr)) -- ++ptr; -- return ptr; -- } -- -- /* -- * Find the end of the line, omitting CR and NL at the end. -- * Returns a pointer to just after the line. -- */ -- static char_u * -- find_line_end(char_u *ptr) -- { -- char_u *s; -- -- s = ptr + STRLEN(ptr); -- while (s > ptr && (s[-1] == CAR || s[-1] == NL)) -- --s; -- return s; -- } -- -- /* -- * Free the list of completions -- */ -- static void -- ins_compl_free(void) -- { -- compl_T *match; -- int i; -- -- VIM_CLEAR(compl_pattern); -- VIM_CLEAR(compl_leader); -- -- if (compl_first_match == NULL) -- return; -- -- ins_compl_del_pum(); -- pum_clear(); -- -- compl_curr_match = compl_first_match; -- do -- { -- match = compl_curr_match; -- compl_curr_match = compl_curr_match->cp_next; -- vim_free(match->cp_str); -- /* several entries may use the same fname, free it just once. */ -- if (match->cp_flags & FREE_FNAME) -- vim_free(match->cp_fname); -- for (i = 0; i < CPT_COUNT; ++i) -- vim_free(match->cp_text[i]); -- vim_free(match); -- } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); -- compl_first_match = compl_curr_match = NULL; -- compl_shown_match = NULL; -- compl_old_match = NULL; -- } -- -- static void -- ins_compl_clear(void) -- { -- compl_cont_status = 0; -- compl_started = FALSE; -- compl_matches = 0; -- VIM_CLEAR(compl_pattern); -- VIM_CLEAR(compl_leader); -- edit_submode_extra = NULL; -- VIM_CLEAR(compl_orig_text); -- compl_enter_selects = FALSE; -- /* clear v:completed_item */ -- set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); -- } -- -- /* -- * Return TRUE when Insert completion is active. -- */ -- int -- ins_compl_active(void) -- { -- return compl_started; -- } -- -- -- /* -- * Get complete information -- */ -- void -- get_complete_info(list_T *what_list, dict_T *retdict) -- { -- int ret = OK; -- listitem_T *item; -- #define CI_WHAT_MODE 0x01 -- #define CI_WHAT_PUM_VISIBLE 0x02 -- #define CI_WHAT_ITEMS 0x04 -- #define CI_WHAT_SELECTED 0x08 -- #define CI_WHAT_INSERTED 0x10 -- #define CI_WHAT_ALL 0xff -- int what_flag; -- -- if (what_list == NULL) -- what_flag = CI_WHAT_ALL; -- else -- { -- what_flag = 0; -- for (item = what_list->lv_first; item != NULL; item = item->li_next) -- { -- char_u *what = tv_get_string(&item->li_tv); -- -- if (STRCMP(what, "mode") == 0) -- what_flag |= CI_WHAT_MODE; -- else if (STRCMP(what, "pum_visible") == 0) -- what_flag |= CI_WHAT_PUM_VISIBLE; -- else if (STRCMP(what, "items") == 0) -- what_flag |= CI_WHAT_ITEMS; -- else if (STRCMP(what, "selected") == 0) -- what_flag |= CI_WHAT_SELECTED; -- else if (STRCMP(what, "inserted") == 0) -- what_flag |= CI_WHAT_INSERTED; -- } -- } -- -- if (ret == OK && (what_flag & CI_WHAT_MODE)) -- ret = dict_add_string(retdict, "mode", ins_compl_mode()); -- -- if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) -- ret = dict_add_number(retdict, "pum_visible", pum_visible()); -- -- if (ret == OK && (what_flag & CI_WHAT_ITEMS)) -- { -- list_T *li; -- dict_T *di; -- compl_T *match; -- -- li = list_alloc(); -- if (li == NULL) -- return; -- ret = dict_add_list(retdict, "items", li); -- if (ret == OK && compl_first_match != NULL) -- { -- match = compl_first_match; -- do -- { -- if (!(match->cp_flags & ORIGINAL_TEXT)) -- { -- di = dict_alloc(); -- if (di == NULL) -- return; -- ret = list_append_dict(li, di); -- if (ret != OK) -- return; -- dict_add_string(di, "word", match->cp_str); -- dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]); -- dict_add_string(di, "menu", match->cp_text[CPT_MENU]); -- dict_add_string(di, "kind", match->cp_text[CPT_KIND]); -- dict_add_string(di, "info", match->cp_text[CPT_INFO]); -- dict_add_string(di, "user_data", -- match->cp_text[CPT_USER_DATA]); -- } -- match = match->cp_next; -- } -- while (match != NULL && match != compl_first_match); -- } -- } -- -- if (ret == OK && (what_flag & CI_WHAT_SELECTED)) -- ret = dict_add_number(retdict, "selected", (compl_curr_match != NULL) ? -- compl_curr_match->cp_number - 1 : -1); -- -- // TODO -- // if (ret == OK && (what_flag & CI_WHAT_INSERTED)) -- } -- -- /* -- * Return Insert completion mode name string -- */ -- static char_u * -- ins_compl_mode(void) -- { -- if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || compl_started) -- return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; -- -- return (char_u *)""; -- } -- -- /* -- * Delete one character before the cursor and show the subset of the matches -- * that match the word that is now before the cursor. -- * Returns the character to be used, NUL if the work is done and another char -- * to be got from the user. -- */ -- static int -- ins_compl_bs(void) -- { -- char_u *line; -- char_u *p; -- -- line = ml_get_curline(); -- p = line + curwin->w_cursor.col; -- MB_PTR_BACK(line, p); -- -- /* Stop completion when the whole word was deleted. For Omni completion -- * allow the word to be deleted, we won't match everything. -- * Respect the 'backspace' option. */ -- if ((int)(p - line) - (int)compl_col < 0 -- || ((int)(p - line) - (int)compl_col == 0 -- && ctrl_x_mode != CTRL_X_OMNI) || ctrl_x_mode == CTRL_X_EVAL -- || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col -- - compl_length < 0)) -- return K_BS; -- -- /* Deleted more than what was used to find matches or didn't finish -- * finding all matches: need to look for matches all over again. */ -- if (curwin->w_cursor.col <= compl_col + compl_length -- || ins_compl_need_restart()) -- ins_compl_restart(); -- -- vim_free(compl_leader); -- compl_leader = vim_strnsave(line + compl_col, (int)(p - line) - compl_col); -- if (compl_leader != NULL) -- { -- ins_compl_new_leader(); -- if (compl_shown_match != NULL) -- /* Make sure current match is not a hidden item. */ -- compl_curr_match = compl_shown_match; -- return NUL; -- } -- return K_BS; -- } -- -- /* -- * Return TRUE when we need to find matches again, ins_compl_restart() is to -- * be called. -- */ -- static int -- ins_compl_need_restart(void) -- { -- /* Return TRUE if we didn't complete finding matches or when the -- * 'completefunc' returned "always" in the "refresh" dictionary item. */ -- return compl_was_interrupted -- || ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) -- && compl_opt_refresh_always); -- } -- -- /* -- * Called after changing "compl_leader". -- * Show the popup menu with a different set of matches. -- * May also search for matches again if the previous search was interrupted. -- */ -- static void -- ins_compl_new_leader(void) -- { -- ins_compl_del_pum(); -- ins_compl_delete(); -- ins_bytes(compl_leader + ins_compl_len()); -- compl_used_match = FALSE; -- -- if (compl_started) -- ins_compl_set_original_text(compl_leader); -- else -- { -- #ifdef FEAT_SPELL -- spell_bad_len = 0; /* need to redetect bad word */ -- #endif -- /* -- * Matches were cleared, need to search for them now. Befor drawing -- * the popup menu display the changed text before the cursor. Set -- * "compl_restarting" to avoid that the first match is inserted. -- */ -- pum_call_update_screen(); -- #ifdef FEAT_GUI -- if (gui.in_use) -- { -- /* Show the cursor after the match, not after the redrawn text. */ -- setcursor(); -- out_flush_cursor(FALSE, FALSE); -- } -- #endif -- compl_restarting = TRUE; -- if (ins_complete(Ctrl_N, TRUE) == FAIL) -- compl_cont_status = 0; -- compl_restarting = FALSE; -- } -- -- compl_enter_selects = !compl_used_match; -- -- /* Show the popup menu with a different set of matches. */ -- ins_compl_show_pum(); -- -- /* Don't let Enter select the original text when there is no popup menu. */ -- if (compl_match_array == NULL) -- compl_enter_selects = FALSE; -- } -- -- /* -- * Return the length of the completion, from the completion start column to -- * the cursor column. Making sure it never goes below zero. -- */ -- static int -- ins_compl_len(void) -- { -- int off = (int)curwin->w_cursor.col - (int)compl_col; -- -- if (off < 0) -- return 0; -- return off; -- } -- -- /* -- * Append one character to the match leader. May reduce the number of -- * matches. -- */ -- static void -- ins_compl_addleader(int c) -- { -- int cc; -- -- if (stop_arrow() == FAIL) -- return; -- if (has_mbyte && (cc = (*mb_char2len)(c)) > 1) -- { -- char_u buf[MB_MAXBYTES + 1]; -- -- (*mb_char2bytes)(c, buf); -- buf[cc] = NUL; -- ins_char_bytes(buf, cc); -- if (compl_opt_refresh_always) -- AppendToRedobuff(buf); -- } -- else -- { -- ins_char(c); -- if (compl_opt_refresh_always) -- AppendCharToRedobuff(c); -- } -- -- /* If we didn't complete finding matches we must search again. */ -- if (ins_compl_need_restart()) -- ins_compl_restart(); -- -- /* When 'always' is set, don't reset compl_leader. While completing, -- * cursor doesn't point original position, changing compl_leader would -- * break redo. */ -- if (!compl_opt_refresh_always) -- { -- vim_free(compl_leader); -- compl_leader = vim_strnsave(ml_get_curline() + compl_col, -- (int)(curwin->w_cursor.col - compl_col)); -- if (compl_leader != NULL) -- ins_compl_new_leader(); -- } -- } -- -- /* -- * Setup for finding completions again without leaving CTRL-X mode. Used when -- * BS or a key was typed while still searching for matches. -- */ -- static void -- ins_compl_restart(void) -- { -- ins_compl_free(); -- compl_started = FALSE; -- compl_matches = 0; -- compl_cont_status = 0; -- compl_cont_mode = 0; -- } -- -- /* -- * Set the first match, the original text. -- */ -- static void -- ins_compl_set_original_text(char_u *str) -- { -- char_u *p; -- -- /* Replace the original text entry. -- * The ORIGINAL_TEXT flag is either at the first item or might possibly be -- * at the last item for backward completion */ -- if (compl_first_match->cp_flags & ORIGINAL_TEXT) /* safety check */ -- { -- p = vim_strsave(str); -- if (p != NULL) -- { -- vim_free(compl_first_match->cp_str); -- compl_first_match->cp_str = p; -- } -- } -- else if (compl_first_match->cp_prev != NULL -- && (compl_first_match->cp_prev->cp_flags & ORIGINAL_TEXT)) -- { -- p = vim_strsave(str); -- if (p != NULL) -- { -- vim_free(compl_first_match->cp_prev->cp_str); -- compl_first_match->cp_prev->cp_str = p; -- } -- } -- } -- -- /* -- * Append one character to the match leader. May reduce the number of -- * matches. -- */ -- static void -- ins_compl_addfrommatch(void) -- { -- char_u *p; -- int len = (int)curwin->w_cursor.col - (int)compl_col; -- int c; -- compl_T *cp; -- -- p = compl_shown_match->cp_str; -- if ((int)STRLEN(p) <= len) /* the match is too short */ -- { -- /* When still at the original match use the first entry that matches -- * the leader. */ -- if (compl_shown_match->cp_flags & ORIGINAL_TEXT) -- { -- p = NULL; -- for (cp = compl_shown_match->cp_next; cp != NULL -- && cp != compl_first_match; cp = cp->cp_next) -- { -- if (compl_leader == NULL -- || ins_compl_equal(cp, compl_leader, -- (int)STRLEN(compl_leader))) -- { -- p = cp->cp_str; -- break; -- } -- } -- if (p == NULL || (int)STRLEN(p) <= len) -- return; -- } -- else -- return; -- } -- p += len; -- c = PTR2CHAR(p); -- ins_compl_addleader(c); -- } -- -- /* -- * Prepare for Insert mode completion, or stop it. -- * Called just after typing a character in Insert mode. -- * Returns TRUE when the character is not to be inserted; -- */ -- static int -- ins_compl_prep(int c) -- { -- char_u *ptr; -- int want_cindent; -- int retval = FALSE; -- -- /* Forget any previous 'special' messages if this is actually -- * a ^X mode key - bar ^R, in which case we wait to see what it gives us. -- */ -- if (c != Ctrl_R && vim_is_ctrl_x_key(c)) -- edit_submode_extra = NULL; -- -- /* Ignore end of Select mode mapping and mouse scroll buttons. */ -- if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP -- || c == K_MOUSELEFT || c == K_MOUSERIGHT) -- return retval; -- -- /* Set "compl_get_longest" when finding the first matches. */ -- if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET -- || (ctrl_x_mode == CTRL_X_NORMAL && !compl_started)) -- { -- compl_get_longest = (strstr((char *)p_cot, "longest") != NULL); -- compl_used_match = TRUE; -- -- } -- -- if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) -- { -- /* -- * We have just typed CTRL-X and aren't quite sure which CTRL-X mode -- * it will be yet. Now we decide. -- */ -- switch (c) -- { -- case Ctrl_E: -- case Ctrl_Y: -- ctrl_x_mode = CTRL_X_SCROLL; -- if (!(State & REPLACE_FLAG)) -- edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); -- else -- edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); -- edit_submode_pre = NULL; -- showmode(); -- break; -- case Ctrl_L: -- ctrl_x_mode = CTRL_X_WHOLE_LINE; -- break; -- case Ctrl_F: -- ctrl_x_mode = CTRL_X_FILES; -- break; -- case Ctrl_K: -- ctrl_x_mode = CTRL_X_DICTIONARY; -- break; -- case Ctrl_R: -- /* Simply allow ^R to happen without affecting ^X mode */ -- break; -- case Ctrl_T: -- ctrl_x_mode = CTRL_X_THESAURUS; -- break; -- #ifdef FEAT_COMPL_FUNC -- case Ctrl_U: -- ctrl_x_mode = CTRL_X_FUNCTION; -- break; -- case Ctrl_O: -- ctrl_x_mode = CTRL_X_OMNI; -- break; -- #endif -- case 's': -- case Ctrl_S: -- ctrl_x_mode = CTRL_X_SPELL; -- #ifdef FEAT_SPELL -- ++emsg_off; /* Avoid getting the E756 error twice. */ -- spell_back_to_badword(); -- --emsg_off; -- #endif -- break; -- case Ctrl_RSB: -- ctrl_x_mode = CTRL_X_TAGS; -- break; -- #ifdef FEAT_FIND_ID -- case Ctrl_I: -- case K_S_TAB: -- ctrl_x_mode = CTRL_X_PATH_PATTERNS; -- break; -- case Ctrl_D: -- ctrl_x_mode = CTRL_X_PATH_DEFINES; -- break; -- #endif -- case Ctrl_V: -- case Ctrl_Q: -- ctrl_x_mode = CTRL_X_CMDLINE; -- break; -- case Ctrl_P: -- case Ctrl_N: -- /* ^X^P means LOCAL expansion if nothing interrupted (eg we -- * just started ^X mode, or there were enough ^X's to cancel -- * the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) -- * do normal expansion when interrupting a different mode (say -- * ^X^F^X^P or ^P^X^X^P, see below) -- * nothing changes if interrupting mode 0, (eg, the flag -- * doesn't change when going to ADDING mode -- Acevedo */ -- if (!(compl_cont_status & CONT_INTRPT)) -- compl_cont_status |= CONT_LOCAL; -- else if (compl_cont_mode != 0) -- compl_cont_status &= ~CONT_LOCAL; -- /* FALLTHROUGH */ -- default: -- /* If we have typed at least 2 ^X's... for modes != 0, we set -- * compl_cont_status = 0 (eg, as if we had just started ^X -- * mode). -- * For mode 0, we set "compl_cont_mode" to an impossible -- * value, in both cases ^X^X can be used to restart the same -- * mode (avoiding ADDING mode). -- * Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start -- * 'complete' and local ^P expansions respectively. -- * In mode 0 an extra ^X is needed since ^X^P goes to ADDING -- * mode -- Acevedo */ -- if (c == Ctrl_X) -- { -- if (compl_cont_mode != 0) -- compl_cont_status = 0; -- else -- compl_cont_mode = CTRL_X_NOT_DEFINED_YET; -- } -- ctrl_x_mode = CTRL_X_NORMAL; -- edit_submode = NULL; -- showmode(); -- break; -- } -- } -- else if (ctrl_x_mode != CTRL_X_NORMAL) -- { -- /* We're already in CTRL-X mode, do we stay in it? */ -- if (!vim_is_ctrl_x_key(c)) -- { -- if (ctrl_x_mode == CTRL_X_SCROLL) -- ctrl_x_mode = CTRL_X_NORMAL; -- else -- ctrl_x_mode = CTRL_X_FINISHED; -- edit_submode = NULL; -- } -- showmode(); -- } -- -- if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) -- { -- /* Show error message from attempted keyword completion (probably -- * 'Pattern not found') until another key is hit, then go back to -- * showing what mode we are in. */ -- showmode(); -- if ((ctrl_x_mode == CTRL_X_NORMAL && c != Ctrl_N && c != Ctrl_P -- && c != Ctrl_R && !ins_compl_pum_key(c)) -- || ctrl_x_mode == CTRL_X_FINISHED) -- { -- /* Get here when we have finished typing a sequence of ^N and -- * ^P or other completion characters in CTRL-X mode. Free up -- * memory that was used, and make sure we can redo the insert. */ -- if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) -- { -- /* -- * If any of the original typed text has been changed, eg when -- * ignorecase is set, we must add back-spaces to the redo -- * buffer. We add as few as necessary to delete just the part -- * of the original text that has changed. -- * When using the longest match, edited the match or used -- * CTRL-E then don't use the current match. -- */ -- if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) -- ptr = compl_curr_match->cp_str; -- else -- ptr = NULL; -- ins_compl_fixRedoBufForLeader(ptr); -- } -- -- #ifdef FEAT_CINDENT -- want_cindent = (can_cindent && cindent_on()); -- #endif -- /* -- * When completing whole lines: fix indent for 'cindent'. -- * Otherwise, break line if it's too long. -- */ -- if (compl_cont_mode == CTRL_X_WHOLE_LINE) -- { -- #ifdef FEAT_CINDENT -- /* re-indent the current line */ -- if (want_cindent) -- { -- do_c_expr_indent(); -- want_cindent = FALSE; /* don't do it again */ -- } -- #endif -- } -- else -- { -- int prev_col = curwin->w_cursor.col; -- -- /* put the cursor on the last char, for 'tw' formatting */ -- if (prev_col > 0) -- dec_cursor(); -- /* only format when something was inserted */ -- if (!arrow_used && !ins_need_undo && c != Ctrl_E) -- insertchar(NUL, 0, -1); -- if (prev_col > 0 -- && ml_get_curline()[curwin->w_cursor.col] != NUL) -- inc_cursor(); -- } -- -- /* If the popup menu is displayed pressing CTRL-Y means accepting -- * the selection without inserting anything. When -- * compl_enter_selects is set the Enter key does the same. */ -- if ((c == Ctrl_Y || (compl_enter_selects -- && (c == CAR || c == K_KENTER || c == NL))) -- && pum_visible()) -- retval = TRUE; -- -- /* CTRL-E means completion is Ended, go back to the typed text. -- * but only do this, if the Popup is still visible */ -- if (c == Ctrl_E) -- { -- ins_compl_delete(); -- if (compl_leader != NULL) -- ins_bytes(compl_leader + ins_compl_len()); -- else if (compl_first_match != NULL) -- ins_bytes(compl_orig_text + ins_compl_len()); -- retval = TRUE; -- } -- -- auto_format(FALSE, TRUE); -- -- ins_compl_free(); -- compl_started = FALSE; -- compl_matches = 0; -- if (!shortmess(SHM_COMPLETIONMENU)) -- msg_clr_cmdline(); /* necessary for "noshowmode" */ -- ctrl_x_mode = CTRL_X_NORMAL; -- compl_enter_selects = FALSE; -- if (edit_submode != NULL) -- { -- edit_submode = NULL; -- showmode(); -- } -- -- #ifdef FEAT_CMDWIN -- if (c == Ctrl_C && cmdwin_type != 0) -- /* Avoid the popup menu remains displayed when leaving the -- * command line window. */ -- update_screen(0); -- #endif -- #ifdef FEAT_CINDENT -- /* -- * Indent now if a key was typed that is in 'cinkeys'. -- */ -- if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) -- do_c_expr_indent(); -- #endif -- /* Trigger the CompleteDone event to give scripts a chance to act -- * upon the completion. */ -- ins_apply_autocmds(EVENT_COMPLETEDONE); -- } -- } -- else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) -- /* Trigger the CompleteDone event to give scripts a chance to act -- * upon the (possibly failed) completion. */ -- ins_apply_autocmds(EVENT_COMPLETEDONE); -- -- /* reset continue_* if we left expansion-mode, if we stay they'll be -- * (re)set properly in ins_complete() */ -- if (!vim_is_ctrl_x_key(c)) -- { -- compl_cont_status = 0; -- compl_cont_mode = 0; -- } -- -- return retval; -- } -- -- /* -- * Fix the redo buffer for the completion leader replacing some of the typed -- * text. This inserts backspaces and appends the changed text. -- * "ptr" is the known leader text or NUL. -- */ -- static void -- ins_compl_fixRedoBufForLeader(char_u *ptr_arg) -- { -- int len; -- char_u *p; -- char_u *ptr = ptr_arg; -- -- if (ptr == NULL) -- { -- if (compl_leader != NULL) -- ptr = compl_leader; -- else -- return; /* nothing to do */ -- } -- if (compl_orig_text != NULL) -- { -- p = compl_orig_text; -- for (len = 0; p[len] != NUL && p[len] == ptr[len]; ++len) -- ; -- if (len > 0) -- len -= (*mb_head_off)(p, p + len); -- for (p += len; *p != NUL; MB_PTR_ADV(p)) -- AppendCharToRedobuff(K_BS); -- } -- else -- len = 0; -- if (ptr != NULL) -- AppendToRedobuffLit(ptr + len, -1); -- } -- -- /* -- * Loops through the list of windows, loaded-buffers or non-loaded-buffers -- * (depending on flag) starting from buf and looking for a non-scanned -- * buffer (other than curbuf). curbuf is special, if it is called with -- * buf=curbuf then it has to be the first call for a given flag/expansion. -- * -- * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo -- */ -- static buf_T * -- ins_compl_next_buf(buf_T *buf, int flag) -- { -- static win_T *wp = NULL; -- -- if (flag == 'w') // just windows -- { -- if (buf == curbuf || wp == NULL) // first call for this flag/expansion -- wp = curwin; -- while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin -- && wp->w_buffer->b_scanned) -- ; -- buf = wp->w_buffer; -- } -- else -- /* 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U' -- * (unlisted buffers) -- * When completing whole lines skip unloaded buffers. */ -- while ((buf = (buf->b_next != NULL ? buf->b_next : firstbuf)) != curbuf -- && ((flag == 'U' -- ? buf->b_p_bl -- : (!buf->b_p_bl -- || (buf->b_ml.ml_mfp == NULL) != (flag == 'u'))) -- || buf->b_scanned)) -- ; -- return buf; -- } -- -- #ifdef FEAT_COMPL_FUNC -- /* -- * Execute user defined complete function 'completefunc' or 'omnifunc', and -- * get matches in "matches". -- */ -- static void -- expand_by_function( -- int type, /* CTRL_X_OMNI or CTRL_X_FUNCTION */ -- char_u *base) -- { -- list_T *matchlist = NULL; -- dict_T *matchdict = NULL; -- typval_T args[3]; -- char_u *funcname; -- pos_T pos; -- win_T *curwin_save; -- buf_T *curbuf_save; -- typval_T rettv; -- int save_State = State; -- -- funcname = (type == CTRL_X_FUNCTION) ? curbuf->b_p_cfu : curbuf->b_p_ofu; -- if (*funcname == NUL) -- return; -- -- /* Call 'completefunc' to obtain the list of matches. */ -- args[0].v_type = VAR_NUMBER; -- args[0].vval.v_number = 0; -- args[1].v_type = VAR_STRING; -- args[1].vval.v_string = base != NULL ? base : (char_u *)""; -- args[2].v_type = VAR_UNKNOWN; -- -- pos = curwin->w_cursor; -- curwin_save = curwin; -- curbuf_save = curbuf; -- -- /* Call a function, which returns a list or dict. */ -- if (call_vim_function(funcname, 2, args, &rettv) == OK) -- { -- switch (rettv.v_type) -- { -- case VAR_LIST: -- matchlist = rettv.vval.v_list; -- break; -- case VAR_DICT: -- matchdict = rettv.vval.v_dict; -- break; -- case VAR_SPECIAL: -- if (rettv.vval.v_number == VVAL_NONE) -- compl_opt_suppress_empty = TRUE; -- // FALLTHROUGH -- default: -- // TODO: Give error message? -- clear_tv(&rettv); -- break; -- } -- } -- -- if (curwin_save != curwin || curbuf_save != curbuf) -- { -- emsg(_(e_complwin)); -- goto theend; -- } -- curwin->w_cursor = pos; /* restore the cursor position */ -- validate_cursor(); -- if (!EQUAL_POS(curwin->w_cursor, pos)) -- { -- emsg(_(e_compldel)); -- goto theend; -- } -- -- if (matchlist != NULL) -- ins_compl_add_list(matchlist); -- else if (matchdict != NULL) -- ins_compl_add_dict(matchdict); -- -- theend: -- // Restore State, it might have been changed. -- State = save_State; -- -- if (matchdict != NULL) -- dict_unref(matchdict); -- if (matchlist != NULL) -- list_unref(matchlist); -- } -- #endif /* FEAT_COMPL_FUNC */ -- -- #if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) || defined(PROTO) -- /* -- * Add completions from a list. -- */ -- static void -- ins_compl_add_list(list_T *list) -- { -- listitem_T *li; -- int dir = compl_direction; -- -- /* Go through the List with matches and add each of them. */ -- for (li = list->lv_first; li != NULL; li = li->li_next) -- { -- if (ins_compl_add_tv(&li->li_tv, dir) == OK) -- /* if dir was BACKWARD then honor it just once */ -- dir = FORWARD; -- else if (did_emsg) -- break; -- } -- } -- -- /* -- * Add completions from a dict. -- */ -- static void -- ins_compl_add_dict(dict_T *dict) -- { -- dictitem_T *di_refresh; -- dictitem_T *di_words; -- -- /* Check for optional "refresh" item. */ -- compl_opt_refresh_always = FALSE; -- di_refresh = dict_find(dict, (char_u *)"refresh", 7); -- if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) -- { -- char_u *v = di_refresh->di_tv.vval.v_string; -- -- if (v != NULL && STRCMP(v, (char_u *)"always") == 0) -- compl_opt_refresh_always = TRUE; -- } -- -- /* Add completions from a "words" list. */ -- di_words = dict_find(dict, (char_u *)"words", 5); -- if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) -- ins_compl_add_list(di_words->di_tv.vval.v_list); -- } -- -- /* -- * Add a match to the list of matches from a typeval_T. -- * 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. -- */ -- int -- ins_compl_add_tv(typval_T *tv, int dir) -- { -- char_u *word; -- int icase = FALSE; -- int adup = FALSE; -- int aempty = FALSE; -- char_u *(cptext[CPT_COUNT]); -- -- if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) -- { -- word = dict_get_string(tv->vval.v_dict, (char_u *)"word", FALSE); -- cptext[CPT_ABBR] = dict_get_string(tv->vval.v_dict, -- (char_u *)"abbr", FALSE); -- cptext[CPT_MENU] = dict_get_string(tv->vval.v_dict, -- (char_u *)"menu", FALSE); -- cptext[CPT_KIND] = dict_get_string(tv->vval.v_dict, -- (char_u *)"kind", FALSE); -- cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict, -- (char_u *)"info", FALSE); -- cptext[CPT_USER_DATA] = dict_get_string(tv->vval.v_dict, -- (char_u *)"user_data", FALSE); -- if (dict_get_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL) -- icase = dict_get_number(tv->vval.v_dict, (char_u *)"icase"); -- if (dict_get_string(tv->vval.v_dict, (char_u *)"dup", FALSE) != NULL) -- adup = dict_get_number(tv->vval.v_dict, (char_u *)"dup"); -- if (dict_get_string(tv->vval.v_dict, (char_u *)"empty", FALSE) != NULL) -- aempty = dict_get_number(tv->vval.v_dict, (char_u *)"empty"); -- } -- else -- { -- word = tv_get_string_chk(tv); -- vim_memset(cptext, 0, sizeof(cptext)); -- } -- if (word == NULL || (!aempty && *word == NUL)) -- return FAIL; -- return ins_compl_add(word, -1, icase, NULL, cptext, dir, 0, adup); -- } -- #endif -- -- /* -- * Get the next expansion(s), using "compl_pattern". -- * The search starts at position "ini" in curbuf and in the direction -- * compl_direction. -- * When "compl_started" is FALSE start at that position, otherwise continue -- * where we stopped searching before. -- * This may return before finding all the matches. -- * Return the total number of matches or -1 if still unknown -- Acevedo -- */ -- static int -- ins_compl_get_exp(pos_T *ini) -- { -- static pos_T first_match_pos; -- static pos_T last_match_pos; -- static char_u *e_cpt = (char_u *)""; /* curr. entry in 'complete' */ -- static int found_all = FALSE; /* Found all matches of a -- certain type. */ -- static buf_T *ins_buf = NULL; /* buffer being scanned */ -- -- pos_T *pos; -- char_u **matches; -- int save_p_scs; -- int save_p_ws; -- int save_p_ic; -- int i; -- int num_matches; -- int len; -- int found_new_match; -- int type = ctrl_x_mode; -- char_u *ptr; -- char_u *dict = NULL; -- int dict_f = 0; -- int set_match_pos; -- -- if (!compl_started) -- { -- FOR_ALL_BUFFERS(ins_buf) -- ins_buf->b_scanned = 0; -- found_all = FALSE; -- ins_buf = curbuf; -- e_cpt = (compl_cont_status & CONT_LOCAL) -- ? (char_u *)"." : curbuf->b_p_cpt; -- last_match_pos = first_match_pos = *ini; -- } -- else if (ins_buf != curbuf && !buf_valid(ins_buf)) -- ins_buf = curbuf; // In case the buffer was wiped out. -- -- compl_old_match = compl_curr_match; /* remember the last current match */ -- pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; -- -- /* -- * For ^N/^P loop over all the flags/windows/buffers in 'complete'. -- */ -- for (;;) -- { -- found_new_match = FAIL; -- set_match_pos = FALSE; -- -- /* For ^N/^P pick a new entry from e_cpt if compl_started is off, -- * or if found_all says this entry is done. For ^X^L only use the -- * entries from 'complete' that look in loaded buffers. */ -- if ((ctrl_x_mode == CTRL_X_NORMAL -- || CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- && (!compl_started || found_all)) -- { -- found_all = FALSE; -- while (*e_cpt == ',' || *e_cpt == ' ') -- e_cpt++; -- if (*e_cpt == '.' && !curbuf->b_scanned) -- { -- ins_buf = curbuf; -- first_match_pos = *ini; -- /* Move the cursor back one character so that ^N can match the -- * word immediately after the cursor. */ -- if (ctrl_x_mode == CTRL_X_NORMAL && dec(&first_match_pos) < 0) -- { -- /* Move the cursor to after the last character in the -- * buffer, so that word at start of buffer is found -- * correctly. */ -- first_match_pos.lnum = ins_buf->b_ml.ml_line_count; -- first_match_pos.col = -- (colnr_T)STRLEN(ml_get(first_match_pos.lnum)); -- } -- last_match_pos = first_match_pos; -- type = 0; -- -- /* Remember the first match so that the loop stops when we -- * wrap and come back there a second time. */ -- set_match_pos = TRUE; -- } -- else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL -- && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) -- { -- /* Scan a buffer, but not the current one. */ -- if (ins_buf->b_ml.ml_mfp != NULL) /* loaded buffer */ -- { -- compl_started = TRUE; -- first_match_pos.col = last_match_pos.col = 0; -- first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; -- last_match_pos.lnum = 0; -- type = 0; -- } -- else /* unloaded buffer, scan like dictionary */ -- { -- found_all = TRUE; -- if (ins_buf->b_fname == NULL) -- continue; -- type = CTRL_X_DICTIONARY; -- dict = ins_buf->b_fname; -- dict_f = DICT_EXACT; -- } -- vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), -- ins_buf->b_fname == NULL -- ? buf_spname(ins_buf) -- : ins_buf->b_sfname == NULL -- ? ins_buf->b_fname -- : ins_buf->b_sfname); -- (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); -- } -- else if (*e_cpt == NUL) -- break; -- else -- { -- if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- type = -1; -- else if (*e_cpt == 'k' || *e_cpt == 's') -- { -- if (*e_cpt == 'k') -- type = CTRL_X_DICTIONARY; -- else -- type = CTRL_X_THESAURUS; -- if (*++e_cpt != ',' && *e_cpt != NUL) -- { -- dict = e_cpt; -- dict_f = DICT_FIRST; -- } -- } -- #ifdef FEAT_FIND_ID -- else if (*e_cpt == 'i') -- type = CTRL_X_PATH_PATTERNS; -- else if (*e_cpt == 'd') -- type = CTRL_X_PATH_DEFINES; -- #endif -- else if (*e_cpt == ']' || *e_cpt == 't') -- { -- type = CTRL_X_TAGS; -- vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags.")); -- (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); -- } -- else -- type = -1; -- -- /* in any case e_cpt is advanced to the next entry */ -- (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ","); -- -- found_all = TRUE; -- if (type == -1) -- continue; -- } -- } -- -- /* If complete() was called then compl_pattern has been reset. The -- * following won't work then, bail out. */ -- if (compl_pattern == NULL) -- break; -- -- switch (type) -- { -- case -1: -- break; -- #ifdef FEAT_FIND_ID -- case CTRL_X_PATH_PATTERNS: -- case CTRL_X_PATH_DEFINES: -- find_pattern_in_path(compl_pattern, compl_direction, -- (int)STRLEN(compl_pattern), FALSE, FALSE, -- (type == CTRL_X_PATH_DEFINES -- && !(compl_cont_status & CONT_SOL)) -- ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND, -- (linenr_T)1, (linenr_T)MAXLNUM); -- break; -- #endif -- -- case CTRL_X_DICTIONARY: -- case CTRL_X_THESAURUS: -- ins_compl_dictionaries( -- dict != NULL ? dict -- : (type == CTRL_X_THESAURUS -- ? (*curbuf->b_p_tsr == NUL -- ? p_tsr -- : curbuf->b_p_tsr) -- : (*curbuf->b_p_dict == NUL -- ? p_dict -- : curbuf->b_p_dict)), -- compl_pattern, -- dict != NULL ? dict_f -- : 0, type == CTRL_X_THESAURUS); -- dict = NULL; -- break; -- -- case CTRL_X_TAGS: -- /* set p_ic according to p_ic, p_scs and pat for find_tags(). */ -- save_p_ic = p_ic; -- p_ic = ignorecase(compl_pattern); -- -- /* Find up to TAG_MANY matches. Avoids that an enormous number -- * of matches is found when compl_pattern is empty */ -- if (find_tags(compl_pattern, &num_matches, &matches, -- TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP -- | (ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0), -- TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) -- { -- ins_compl_add_matches(num_matches, matches, p_ic); -- } -- p_ic = save_p_ic; -- break; -- -- case CTRL_X_FILES: -- if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, -- EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) -- { -- -- /* May change home directory back to "~". */ -- tilde_replace(compl_pattern, num_matches, matches); -- ins_compl_add_matches(num_matches, matches, p_fic || p_wic); -- } -- break; -- -- case CTRL_X_CMDLINE: -- if (expand_cmdline(&compl_xp, compl_pattern, -- (int)STRLEN(compl_pattern), -- &num_matches, &matches) == EXPAND_OK) -- ins_compl_add_matches(num_matches, matches, FALSE); -- break; -- -- #ifdef FEAT_COMPL_FUNC -- case CTRL_X_FUNCTION: -- case CTRL_X_OMNI: -- expand_by_function(type, compl_pattern); -- break; -- #endif -- -- case CTRL_X_SPELL: -- #ifdef FEAT_SPELL -- num_matches = expand_spelling(first_match_pos.lnum, -- compl_pattern, &matches); -- if (num_matches > 0) -- ins_compl_add_matches(num_matches, matches, p_ic); -- #endif -- break; -- -- default: /* normal ^P/^N and ^X^L */ -- /* -- * If 'infercase' is set, don't use 'smartcase' here -- */ -- save_p_scs = p_scs; -- if (ins_buf->b_p_inf) -- p_scs = FALSE; -- -- /* Buffers other than curbuf are scanned from the beginning or the -- * end but never from the middle, thus setting nowrapscan in this -- * buffers is a good idea, on the other hand, we always set -- * wrapscan for curbuf to avoid missing matches -- Acevedo,Webb */ -- save_p_ws = p_ws; -- if (ins_buf != curbuf) -- p_ws = FALSE; -- else if (*e_cpt == '.') -- p_ws = TRUE; -- for (;;) -- { -- int flags = 0; -- -- ++msg_silent; /* Don't want messages for wrapscan. */ -- -- /* CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode) -- * || word-wise search that -- * has added a word that was at the beginning of the line */ -- if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode) -- || (compl_cont_status & CONT_SOL)) -- found_new_match = search_for_exact_line(ins_buf, pos, -- compl_direction, compl_pattern); -- else -- found_new_match = searchit(NULL, ins_buf, pos, NULL, -- compl_direction, -- compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, -- RE_LAST, (linenr_T)0, NULL, NULL); -- --msg_silent; -- if (!compl_started || set_match_pos) -- { -- /* set "compl_started" even on fail */ -- compl_started = TRUE; -- first_match_pos = *pos; -- last_match_pos = *pos; -- set_match_pos = FALSE; -- } -- else if (first_match_pos.lnum == last_match_pos.lnum -- && first_match_pos.col == last_match_pos.col) -- found_new_match = FAIL; -- if (found_new_match == FAIL) -- { -- if (ins_buf == curbuf) -- found_all = TRUE; -- break; -- } -- -- /* when ADDING, the text before the cursor matches, skip it */ -- if ( (compl_cont_status & CONT_ADDING) && ins_buf == curbuf -- && ini->lnum == pos->lnum -- && ini->col == pos->col) -- continue; -- ptr = ml_get_buf(ins_buf, pos->lnum, FALSE) + pos->col; -- if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- { -- if (compl_cont_status & CONT_ADDING) -- { -- if (pos->lnum >= ins_buf->b_ml.ml_line_count) -- continue; -- ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); -- if (!p_paste) -- ptr = skipwhite(ptr); -- } -- len = (int)STRLEN(ptr); -- } -- else -- { -- char_u *tmp_ptr = ptr; -- -- if (compl_cont_status & CONT_ADDING) -- { -- tmp_ptr += compl_length; -- /* Skip if already inside a word. */ -- if (vim_iswordp(tmp_ptr)) -- continue; -- /* Find start of next word. */ -- tmp_ptr = find_word_start(tmp_ptr); -- } -- /* Find end of this word. */ -- tmp_ptr = find_word_end(tmp_ptr); -- len = (int)(tmp_ptr - ptr); -- -- if ((compl_cont_status & CONT_ADDING) -- && len == compl_length) -- { -- if (pos->lnum < ins_buf->b_ml.ml_line_count) -- { -- /* Try next line, if any. the new word will be -- * "join" as if the normal command "J" was used. -- * IOSIZE is always greater than -- * compl_length, so the next STRNCPY always -- * works -- Acevedo */ -- STRNCPY(IObuff, ptr, len); -- ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); -- tmp_ptr = ptr = skipwhite(ptr); -- /* Find start of next word. */ -- tmp_ptr = find_word_start(tmp_ptr); -- /* Find end of next word. */ -- tmp_ptr = find_word_end(tmp_ptr); -- if (tmp_ptr > ptr) -- { -- if (*ptr != ')' && IObuff[len - 1] != TAB) -- { -- if (IObuff[len - 1] != ' ') -- IObuff[len++] = ' '; -- /* IObuf =~ "\k.* ", thus len >= 2 */ -- if (p_js -- && (IObuff[len - 2] == '.' -- || (vim_strchr(p_cpo, CPO_JOINSP) -- == NULL -- && (IObuff[len - 2] == '?' -- || IObuff[len - 2] == '!')))) -- IObuff[len++] = ' '; -- } -- /* copy as much as possible of the new word */ -- if (tmp_ptr - ptr >= IOSIZE - len) -- tmp_ptr = ptr + IOSIZE - len - 1; -- STRNCPY(IObuff + len, ptr, tmp_ptr - ptr); -- len += (int)(tmp_ptr - ptr); -- flags |= CONT_S_IPOS; -- } -- IObuff[len] = NUL; -- ptr = IObuff; -- } -- if (len == compl_length) -- continue; -- } -- } -- if (ins_compl_add_infercase(ptr, len, p_ic, -- ins_buf == curbuf ? NULL : ins_buf->b_sfname, -- 0, flags) != NOTDONE) -- { -- found_new_match = OK; -- break; -- } -- } -- p_scs = save_p_scs; -- p_ws = save_p_ws; -- } -- -- /* check if compl_curr_match has changed, (e.g. other type of -- * expansion added something) */ -- if (type != 0 && compl_curr_match != compl_old_match) -- found_new_match = OK; -- -- /* break the loop for specialized modes (use 'complete' just for the -- * generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new -- * match */ -- if ((ctrl_x_mode != CTRL_X_NORMAL -- && !CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- || found_new_match != FAIL) -- { -- if (got_int) -- break; -- /* Fill the popup menu as soon as possible. */ -- if (type != -1) -- ins_compl_check_keys(0, FALSE); -- -- if ((ctrl_x_mode != CTRL_X_NORMAL -- && !CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- || compl_interrupted) -- break; -- compl_started = TRUE; -- } -- else -- { -- /* Mark a buffer scanned when it has been scanned completely */ -- if (type == 0 || type == CTRL_X_PATH_PATTERNS) -- ins_buf->b_scanned = TRUE; -- -- compl_started = FALSE; -- } -- } -- compl_started = TRUE; -- -- if ((ctrl_x_mode == CTRL_X_NORMAL || CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- && *e_cpt == NUL) /* Got to end of 'complete' */ -- found_new_match = FAIL; -- -- i = -1; /* total of matches, unknown */ -- if (found_new_match == FAIL || (ctrl_x_mode != CTRL_X_NORMAL -- && !CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode))) -- i = ins_compl_make_cyclic(); -- -- if (compl_old_match != NULL) -- { -- /* If several matches were added (FORWARD) or the search failed and has -- * just been made cyclic then we have to move compl_curr_match to the -- * next or previous entry (if any) -- Acevedo */ -- compl_curr_match = compl_direction == FORWARD ? compl_old_match->cp_next -- : compl_old_match->cp_prev; -- if (compl_curr_match == NULL) -- compl_curr_match = compl_old_match; -- } -- return i; -- } -- -- /* Delete the old text being completed. */ -- static void -- ins_compl_delete(void) -- { -- int col; -- -- /* -- * In insert mode: Delete the typed part. -- * In replace mode: Put the old characters back, if any. -- */ -- col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); -- if ((int)curwin->w_cursor.col > col) -- { -- if (stop_arrow() == FAIL) -- return; -- backspace_until_column(col); -- } -- -- /* TODO: is this sufficient for redrawing? Redrawing everything causes -- * flicker, thus we can't do that. */ -- changed_cline_bef_curs(); -- /* clear v:completed_item */ -- set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); -- } -- -- /* -- * Insert the new text being completed. -- * "in_compl_func" is TRUE when called from complete_check(). -- */ -- static void -- ins_compl_insert(int in_compl_func) -- { -- dict_T *dict; -- -- ins_bytes(compl_shown_match->cp_str + ins_compl_len()); -- if (compl_shown_match->cp_flags & ORIGINAL_TEXT) -- compl_used_match = FALSE; -- else -- compl_used_match = TRUE; -- -- /* Set completed item. */ -- /* { word, abbr, menu, kind, info } */ -- dict = dict_alloc_lock(VAR_FIXED); -- if (dict != NULL) -- { -- dict_add_string(dict, "word", compl_shown_match->cp_str); -- dict_add_string(dict, "abbr", compl_shown_match->cp_text[CPT_ABBR]); -- dict_add_string(dict, "menu", compl_shown_match->cp_text[CPT_MENU]); -- dict_add_string(dict, "kind", compl_shown_match->cp_text[CPT_KIND]); -- dict_add_string(dict, "info", compl_shown_match->cp_text[CPT_INFO]); -- dict_add_string(dict, "user_data", -- compl_shown_match->cp_text[CPT_USER_DATA]); -- } -- set_vim_var_dict(VV_COMPLETED_ITEM, dict); -- if (!in_compl_func) -- compl_curr_match = compl_shown_match; -- } -- -- /* -- * Fill in the next completion in the current direction. -- * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to -- * get more completions. If it is FALSE, then we just do nothing when there -- * are no more completions in a given direction. The latter case is used when -- * we are still in the middle of finding completions, to allow browsing -- * through the ones found so far. -- * Return the total number of matches, or -1 if still unknown -- webb. -- * -- * compl_curr_match is currently being used by ins_compl_get_exp(), so we use -- * compl_shown_match here. -- * -- * Note that this function may be called recursively once only. First with -- * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn -- * calls this function with "allow_get_expansion" FALSE. -- */ -- static int -- ins_compl_next( -- int allow_get_expansion, -- int count, /* repeat completion this many times; should -- be at least 1 */ -- int insert_match, /* Insert the newly selected match */ -- int in_compl_func) /* called from complete_check() */ -- { -- int num_matches = -1; -- int todo = count; -- compl_T *found_compl = NULL; -- int found_end = FALSE; -- int advance; -- int started = compl_started; -- -- /* When user complete function return -1 for findstart which is next -- * time of 'always', compl_shown_match become NULL. */ -- if (compl_shown_match == NULL) -- return -1; -- -- if (compl_leader != NULL -- && (compl_shown_match->cp_flags & ORIGINAL_TEXT) == 0) -- { -- /* Set "compl_shown_match" to the actually shown match, it may differ -- * when "compl_leader" is used to omit some of the matches. */ -- while (!ins_compl_equal(compl_shown_match, -- compl_leader, (int)STRLEN(compl_leader)) -- && compl_shown_match->cp_next != NULL -- && compl_shown_match->cp_next != compl_first_match) -- compl_shown_match = compl_shown_match->cp_next; -- -- /* If we didn't find it searching forward, and compl_shows_dir is -- * backward, find the last match. */ -- if (compl_shows_dir == BACKWARD -- && !ins_compl_equal(compl_shown_match, -- compl_leader, (int)STRLEN(compl_leader)) -- && (compl_shown_match->cp_next == NULL -- || compl_shown_match->cp_next == compl_first_match)) -- { -- while (!ins_compl_equal(compl_shown_match, -- compl_leader, (int)STRLEN(compl_leader)) -- && compl_shown_match->cp_prev != NULL -- && compl_shown_match->cp_prev != compl_first_match) -- compl_shown_match = compl_shown_match->cp_prev; -- } -- } -- -- if (allow_get_expansion && insert_match -- && (!(compl_get_longest || compl_restarting) || compl_used_match)) -- /* Delete old text to be replaced */ -- ins_compl_delete(); -- -- /* When finding the longest common text we stick at the original text, -- * don't let CTRL-N or CTRL-P move to the first match. */ -- advance = count != 1 || !allow_get_expansion || !compl_get_longest; -- -- /* When restarting the search don't insert the first match either. */ -- if (compl_restarting) -- { -- advance = FALSE; -- compl_restarting = FALSE; -- } -- -- /* Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap -- * around. */ -- while (--todo >= 0) -- { -- if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) -- { -- compl_shown_match = compl_shown_match->cp_next; -- found_end = (compl_first_match != NULL -- && (compl_shown_match->cp_next == compl_first_match -- || compl_shown_match == compl_first_match)); -- } -- else if (compl_shows_dir == BACKWARD -- && compl_shown_match->cp_prev != NULL) -- { -- found_end = (compl_shown_match == compl_first_match); -- compl_shown_match = compl_shown_match->cp_prev; -- found_end |= (compl_shown_match == compl_first_match); -- } -- else -- { -- if (!allow_get_expansion) -- { -- if (advance) -- { -- if (compl_shows_dir == BACKWARD) -- compl_pending -= todo + 1; -- else -- compl_pending += todo + 1; -- } -- return -1; -- } -- -- if (!compl_no_select && advance) -- { -- if (compl_shows_dir == BACKWARD) -- --compl_pending; -- else -- ++compl_pending; -- } -- -- /* Find matches. */ -- num_matches = ins_compl_get_exp(&compl_startpos); -- -- /* handle any pending completions */ -- while (compl_pending != 0 && compl_direction == compl_shows_dir -- && advance) -- { -- if (compl_pending > 0 && compl_shown_match->cp_next != NULL) -- { -- compl_shown_match = compl_shown_match->cp_next; -- --compl_pending; -- } -- if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) -- { -- compl_shown_match = compl_shown_match->cp_prev; -- ++compl_pending; -- } -- else -- break; -- } -- found_end = FALSE; -- } -- if ((compl_shown_match->cp_flags & ORIGINAL_TEXT) == 0 -- && compl_leader != NULL -- && !ins_compl_equal(compl_shown_match, -- compl_leader, (int)STRLEN(compl_leader))) -- ++todo; -- else -- /* Remember a matching item. */ -- found_compl = compl_shown_match; -- -- /* Stop at the end of the list when we found a usable match. */ -- if (found_end) -- { -- if (found_compl != NULL) -- { -- compl_shown_match = found_compl; -- break; -- } -- todo = 1; /* use first usable match after wrapping around */ -- } -- } -- -- /* Insert the text of the new completion, or the compl_leader. */ -- if (compl_no_insert && !started) -- { -- ins_bytes(compl_orig_text + ins_compl_len()); -- compl_used_match = FALSE; -- } -- else if (insert_match) -- { -- if (!compl_get_longest || compl_used_match) -- ins_compl_insert(in_compl_func); -- else -- ins_bytes(compl_leader + ins_compl_len()); -- } -- else -- compl_used_match = FALSE; -- -- if (!allow_get_expansion) -- { -- /* may undisplay the popup menu first */ -- ins_compl_upd_pum(); -- -- if (pum_enough_matches()) -- // Will display the popup menu, don't redraw yet to avoid flicker. -- pum_call_update_screen(); -- else -- // Not showing the popup menu yet, redraw to show the user what was -- // inserted. -- update_screen(0); -- -- /* display the updated popup menu */ -- ins_compl_show_pum(); -- #ifdef FEAT_GUI -- if (gui.in_use) -- { -- /* Show the cursor after the match, not after the redrawn text. */ -- setcursor(); -- out_flush_cursor(FALSE, FALSE); -- } -- #endif -- -- /* Delete old text to be replaced, since we're still searching and -- * don't want to match ourselves! */ -- ins_compl_delete(); -- } -- -- /* Enter will select a match when the match wasn't inserted and the popup -- * menu is visible. */ -- if (compl_no_insert && !started) -- compl_enter_selects = TRUE; -- else -- compl_enter_selects = !insert_match && compl_match_array != NULL; -- -- /* -- * Show the file name for the match (if any) -- * Truncate the file name to avoid a wait for return. -- */ -- if (compl_shown_match->cp_fname != NULL) -- { -- char *lead = _("match in file"); -- int space = sc_col - vim_strsize((char_u *)lead) - 2; -- char_u *s; -- char_u *e; -- -- if (space > 0) -- { -- /* We need the tail that fits. With double-byte encoding going -- * back from the end is very slow, thus go from the start and keep -- * the text that fits in "space" between "s" and "e". */ -- for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) -- { -- space -= ptr2cells(e); -- while (space < 0) -- { -- space += ptr2cells(s); -- MB_PTR_ADV(s); -- } -- } -- vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, -- s > compl_shown_match->cp_fname ? "<" : "", s); -- msg((char *)IObuff); -- redraw_cmdline = FALSE; /* don't overwrite! */ -- } -- } -- -- return num_matches; -- } -- -- /* -- * Call this while finding completions, to check whether the user has hit a key -- * that should change the currently displayed completion, or exit completion -- * mode. Also, when compl_pending is not zero, show a completion as soon as -- * possible. -- webb -- * "frequency" specifies out of how many calls we actually check. -- * "in_compl_func" is TRUE when called from complete_check(), don't set -- * compl_curr_match. -- */ -- void -- ins_compl_check_keys(int frequency, int in_compl_func) -- { -- static int count = 0; -- int c; -- -- /* Don't check when reading keys from a script, :normal or feedkeys(). -- * That would break the test scripts. But do check for keys when called -- * from complete_check(). */ -- if (!in_compl_func && (using_script() || ex_normal_busy)) -- return; -- -- /* Only do this at regular intervals */ -- if (++count < frequency) -- return; -- count = 0; -- -- /* Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key() -- * can't do its work correctly. */ -- c = vpeekc_any(); -- if (c != NUL) -- { -- if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) -- { -- c = safe_vgetc(); /* Eat the character */ -- compl_shows_dir = ins_compl_key2dir(c); -- (void)ins_compl_next(FALSE, ins_compl_key2count(c), -- c != K_UP && c != K_DOWN, in_compl_func); -- } -- else -- { -- /* Need to get the character to have KeyTyped set. We'll put it -- * back with vungetc() below. But skip K_IGNORE. */ -- c = safe_vgetc(); -- if (c != K_IGNORE) -- { -- /* Don't interrupt completion when the character wasn't typed, -- * e.g., when doing @q to replay keys. */ -- if (c != Ctrl_R && KeyTyped) -- compl_interrupted = TRUE; -- -- vungetc(c); -- } -- } -- } -- if (compl_pending != 0 && !got_int && !compl_no_insert) -- { -- int todo = compl_pending > 0 ? compl_pending : -compl_pending; -- -- compl_pending = 0; -- (void)ins_compl_next(FALSE, todo, TRUE, in_compl_func); -- } -- } -- -- /* -- * Decide the direction of Insert mode complete from the key typed. -- * Returns BACKWARD or FORWARD. -- */ -- static int -- ins_compl_key2dir(int c) -- { -- if (c == Ctrl_P || c == Ctrl_L -- || c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP || c == K_UP) -- return BACKWARD; -- return FORWARD; -- } -- -- /* -- * Return TRUE for keys that are used for completion only when the popup menu -- * is visible. -- */ -- static int -- ins_compl_pum_key(int c) -- { -- return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP -- || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == K_S_DOWN -- || c == K_UP || c == K_DOWN); -- } -- -- /* -- * Decide the number of completions to move forward. -- * Returns 1 for most keys, height of the popup menu for page-up/down keys. -- */ -- static int -- ins_compl_key2count(int c) -- { -- int h; -- -- if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) -- { -- h = pum_get_height(); -- if (h > 3) -- h -= 2; /* keep some context */ -- return h; -- } -- return 1; -- } -- -- /* -- * Return TRUE if completion with "c" should insert the match, FALSE if only -- * to change the currently selected completion. -- */ -- static int -- ins_compl_use_match(int c) -- { -- switch (c) -- { -- case K_UP: -- case K_DOWN: -- case K_PAGEDOWN: -- case K_KPAGEDOWN: -- case K_S_DOWN: -- case K_PAGEUP: -- case K_KPAGEUP: -- case K_S_UP: -- return FALSE; -- } -- return TRUE; -- } -- -- /* -- * Do Insert mode completion. -- * Called when character "c" was typed, which has a meaning for completion. -- * Returns OK if completion was done, FAIL if something failed (out of mem). -- */ -- static int -- ins_complete(int c, int enable_pum) -- { -- char_u *line; -- int startcol = 0; /* column where searched text starts */ -- colnr_T curs_col; /* cursor column */ -- int n; -- int save_w_wrow; -- int save_w_leftcol; -- int insert_match; -- int save_did_ai = did_ai; -- -- compl_direction = ins_compl_key2dir(c); -- insert_match = ins_compl_use_match(c); -- -- if (!compl_started) -- { -- /* First time we hit ^N or ^P (in a row, I mean) */ -- -- did_ai = FALSE; -- #ifdef FEAT_SMARTINDENT -- did_si = FALSE; -- can_si = FALSE; -- can_si_back = FALSE; -- #endif -- if (stop_arrow() == FAIL) -- return FAIL; -- -- line = ml_get(curwin->w_cursor.lnum); -- curs_col = curwin->w_cursor.col; -- compl_pending = 0; -- -- /* If this same ctrl_x_mode has been interrupted use the text from -- * "compl_startpos" to the cursor as a pattern to add a new word -- * instead of expand the one before the cursor, in word-wise if -- * "compl_startpos" is not in the same line as the cursor then fix it -- * (the line has been split because it was longer than 'tw'). if SOL -- * is set then skip the previous pattern, a word at the beginning of -- * the line has been inserted, we'll look for that -- Acevedo. */ -- if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT -- && compl_cont_mode == ctrl_x_mode) -- { -- /* -- * it is a continued search -- */ -- compl_cont_status &= ~CONT_INTRPT; /* remove INTRPT */ -- if (ctrl_x_mode == CTRL_X_NORMAL -- || ctrl_x_mode == CTRL_X_PATH_PATTERNS -- || ctrl_x_mode == CTRL_X_PATH_DEFINES) -- { -- if (compl_startpos.lnum != curwin->w_cursor.lnum) -- { -- /* line (probably) wrapped, set compl_startpos to the -- * first non_blank in the line, if it is not a wordchar -- * include it to get a better pattern, but then we don't -- * want the "\\<" prefix, check it bellow */ -- compl_col = (colnr_T)getwhitecols(line); -- compl_startpos.col = compl_col; -- compl_startpos.lnum = curwin->w_cursor.lnum; -- compl_cont_status &= ~CONT_SOL; /* clear SOL if present */ -- } -- else -- { -- /* S_IPOS was set when we inserted a word that was at the -- * beginning of the line, which means that we'll go to SOL -- * mode but first we need to redefine compl_startpos */ -- if (compl_cont_status & CONT_S_IPOS) -- { -- compl_cont_status |= CONT_SOL; -- compl_startpos.col = (colnr_T)(skipwhite( -- line + compl_length -- + compl_startpos.col) - line); -- } -- compl_col = compl_startpos.col; -- } -- compl_length = curwin->w_cursor.col - (int)compl_col; -- /* IObuff is used to add a "word from the next line" would we -- * have enough space? just being paranoid */ -- #define MIN_SPACE 75 -- if (compl_length > (IOSIZE - MIN_SPACE)) -- { -- compl_cont_status &= ~CONT_SOL; -- compl_length = (IOSIZE - MIN_SPACE); -- compl_col = curwin->w_cursor.col - compl_length; -- } -- compl_cont_status |= CONT_ADDING | CONT_N_ADDS; -- if (compl_length < 1) -- compl_cont_status &= CONT_LOCAL; -- } -- else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- compl_cont_status = CONT_ADDING | CONT_N_ADDS; -- else -- compl_cont_status = 0; -- } -- else -- compl_cont_status &= CONT_LOCAL; -- -- if (!(compl_cont_status & CONT_ADDING)) /* normal expansion */ -- { -- compl_cont_mode = ctrl_x_mode; -- if (ctrl_x_mode != CTRL_X_NORMAL) -- /* Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL */ -- compl_cont_status = 0; -- compl_cont_status |= CONT_N_ADDS; -- compl_startpos = curwin->w_cursor; -- startcol = (int)curs_col; -- compl_col = 0; -- } -- -- /* Work out completion pattern and original text -- webb */ -- if (ctrl_x_mode == CTRL_X_NORMAL || (ctrl_x_mode & CTRL_X_WANT_IDENT)) -- { -- if ((compl_cont_status & CONT_SOL) -- || ctrl_x_mode == CTRL_X_PATH_DEFINES) -- { -- if (!(compl_cont_status & CONT_ADDING)) -- { -- while (--startcol >= 0 && vim_isIDc(line[startcol])) -- ; -- compl_col += ++startcol; -- compl_length = curs_col - startcol; -- } -- if (p_ic) -- compl_pattern = str_foldcase(line + compl_col, -- compl_length, NULL, 0); -- else -- compl_pattern = vim_strnsave(line + compl_col, -- compl_length); -- if (compl_pattern == NULL) -- return FAIL; -- } -- else if (compl_cont_status & CONT_ADDING) -- { -- char_u *prefix = (char_u *)"\\<"; -- -- /* we need up to 2 extra chars for the prefix */ -- compl_pattern = alloc(quote_meta(NULL, line + compl_col, -- compl_length) + 2); -- if (compl_pattern == NULL) -- return FAIL; -- if (!vim_iswordp(line + compl_col) -- || (compl_col > 0 -- && (vim_iswordp(mb_prevptr(line, line + compl_col))))) -- prefix = (char_u *)""; -- STRCPY((char *)compl_pattern, prefix); -- (void)quote_meta(compl_pattern + STRLEN(prefix), -- line + compl_col, compl_length); -- } -- else if (--startcol < 0 -- || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) -- { -- /* Match any word of at least two chars */ -- compl_pattern = vim_strsave((char_u *)"\\<\\k\\k"); -- if (compl_pattern == NULL) -- return FAIL; -- compl_col += curs_col; -- compl_length = 0; -- } -- else -- { -- /* Search the point of change class of multibyte character -- * or not a word single byte character backward. */ -- if (has_mbyte) -- { -- int base_class; -- int head_off; -- -- startcol -= (*mb_head_off)(line, line + startcol); -- base_class = mb_get_class(line + startcol); -- while (--startcol >= 0) -- { -- head_off = (*mb_head_off)(line, line + startcol); -- if (base_class != mb_get_class(line + startcol -- - head_off)) -- break; -- startcol -= head_off; -- } -- } -- else -- while (--startcol >= 0 && vim_iswordc(line[startcol])) -- ; -- compl_col += ++startcol; -- compl_length = (int)curs_col - startcol; -- if (compl_length == 1) -- { -- /* Only match word with at least two chars -- webb -- * there's no need to call quote_meta, -- * alloc(7) is enough -- Acevedo -- */ -- compl_pattern = alloc(7); -- if (compl_pattern == NULL) -- return FAIL; -- STRCPY((char *)compl_pattern, "\\<"); -- (void)quote_meta(compl_pattern + 2, line + compl_col, 1); -- STRCAT((char *)compl_pattern, "\\k"); -- } -- else -- { -- compl_pattern = alloc(quote_meta(NULL, line + compl_col, -- compl_length) + 2); -- if (compl_pattern == NULL) -- return FAIL; -- STRCPY((char *)compl_pattern, "\\<"); -- (void)quote_meta(compl_pattern + 2, line + compl_col, -- compl_length); -- } -- } -- } -- else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- { -- compl_col = (colnr_T)getwhitecols(line); -- compl_length = (int)curs_col - (int)compl_col; -- if (compl_length < 0) /* cursor in indent: empty pattern */ -- compl_length = 0; -- if (p_ic) -- compl_pattern = str_foldcase(line + compl_col, compl_length, -- NULL, 0); -- else -- compl_pattern = vim_strnsave(line + compl_col, compl_length); -- if (compl_pattern == NULL) -- return FAIL; -- } -- else if (ctrl_x_mode == CTRL_X_FILES) -- { -- /* Go back to just before the first filename character. */ -- if (startcol > 0) -- { -- char_u *p = line + startcol; -- -- MB_PTR_BACK(line, p); -- while (p > line && vim_isfilec(PTR2CHAR(p))) -- MB_PTR_BACK(line, p); -- if (p == line && vim_isfilec(PTR2CHAR(p))) -- startcol = 0; -- else -- startcol = (int)(p - line) + 1; -- } -- -- compl_col += startcol; -- compl_length = (int)curs_col - startcol; -- compl_pattern = addstar(line + compl_col, compl_length, -- EXPAND_FILES); -- if (compl_pattern == NULL) -- return FAIL; -- } -- else if (ctrl_x_mode == CTRL_X_CMDLINE) -- { -- compl_pattern = vim_strnsave(line, curs_col); -- if (compl_pattern == NULL) -- return FAIL; -- set_cmd_context(&compl_xp, compl_pattern, -- (int)STRLEN(compl_pattern), curs_col, FALSE); -- if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL -- || compl_xp.xp_context == EXPAND_NOTHING) -- /* No completion possible, use an empty pattern to get a -- * "pattern not found" message. */ -- compl_col = curs_col; -- else -- compl_col = (int)(compl_xp.xp_pattern - compl_pattern); -- compl_length = curs_col - compl_col; -- } -- else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) -- { -- #ifdef FEAT_COMPL_FUNC -- /* -- * Call user defined function 'completefunc' with "a:findstart" -- * set to 1 to obtain the length of text to use for completion. -- */ -- typval_T args[3]; -- int col; -- char_u *funcname; -- pos_T pos; -- win_T *curwin_save; -- buf_T *curbuf_save; -- int save_State = State; -- -- /* Call 'completefunc' or 'omnifunc' and get pattern length as a -- * string */ -- funcname = ctrl_x_mode == CTRL_X_FUNCTION -- ? curbuf->b_p_cfu : curbuf->b_p_ofu; -- if (*funcname == NUL) -- { -- semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION -- ? "completefunc" : "omnifunc"); -- /* restore did_ai, so that adding comment leader works */ -- did_ai = save_did_ai; -- return FAIL; -- } -- -- args[0].v_type = VAR_NUMBER; -- args[0].vval.v_number = 1; -- args[1].v_type = VAR_STRING; -- args[1].vval.v_string = (char_u *)""; -- args[2].v_type = VAR_UNKNOWN; -- pos = curwin->w_cursor; -- curwin_save = curwin; -- curbuf_save = curbuf; -- col = call_func_retnr(funcname, 2, args); -- -- State = save_State; -- if (curwin_save != curwin || curbuf_save != curbuf) -- { -- emsg(_(e_complwin)); -- return FAIL; -- } -- curwin->w_cursor = pos; /* restore the cursor position */ -- validate_cursor(); -- if (!EQUAL_POS(curwin->w_cursor, pos)) -- { -- emsg(_(e_compldel)); -- return FAIL; -- } -- -- /* Return value -2 means the user complete function wants to -- * cancel the complete without an error. -- * Return value -3 does the same as -2 and leaves CTRL-X mode.*/ -- if (col == -2) -- return FAIL; -- if (col == -3) -- { -- ctrl_x_mode = CTRL_X_NORMAL; -- edit_submode = NULL; -- if (!shortmess(SHM_COMPLETIONMENU)) -- msg_clr_cmdline(); -- return FAIL; -- } -- -- /* -- * Reset extended parameters of completion, when start new -- * completion. -- */ -- compl_opt_refresh_always = FALSE; -- compl_opt_suppress_empty = FALSE; -- -- if (col < 0) -- col = curs_col; -- compl_col = col; -- if (compl_col > curs_col) -- compl_col = curs_col; -- -- /* Setup variables for completion. Need to obtain "line" again, -- * it may have become invalid. */ -- line = ml_get(curwin->w_cursor.lnum); -- compl_length = curs_col - compl_col; -- compl_pattern = vim_strnsave(line + compl_col, compl_length); -- if (compl_pattern == NULL) -- #endif -- return FAIL; -- } -- else if (ctrl_x_mode == CTRL_X_SPELL) -- { -- #ifdef FEAT_SPELL -- if (spell_bad_len > 0) -- compl_col = curs_col - spell_bad_len; -- else -- compl_col = spell_word_start(startcol); -- if (compl_col >= (colnr_T)startcol) -- { -- compl_length = 0; -- compl_col = curs_col; -- } -- else -- { -- spell_expand_check_cap(compl_col); -- compl_length = (int)curs_col - compl_col; -- } -- /* Need to obtain "line" again, it may have become invalid. */ -- line = ml_get(curwin->w_cursor.lnum); -- compl_pattern = vim_strnsave(line + compl_col, compl_length); -- if (compl_pattern == NULL) -- #endif -- return FAIL; -- } -- else -- { -- internal_error("ins_complete()"); -- return FAIL; -- } -- -- if (compl_cont_status & CONT_ADDING) -- { -- edit_submode_pre = (char_u *)_(" Adding"); -- if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) -- { -- /* Insert a new line, keep indentation but ignore 'comments' */ -- #ifdef FEAT_COMMENTS -- char_u *old = curbuf->b_p_com; -- -- curbuf->b_p_com = (char_u *)""; -- #endif -- compl_startpos.lnum = curwin->w_cursor.lnum; -- compl_startpos.col = compl_col; -- ins_eol('\r'); -- #ifdef FEAT_COMMENTS -- curbuf->b_p_com = old; -- #endif -- compl_length = 0; -- compl_col = curwin->w_cursor.col; -- } -- } -- else -- { -- edit_submode_pre = NULL; -- compl_startpos.col = compl_col; -- } -- -- if (compl_cont_status & CONT_LOCAL) -- edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); -- else -- edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); -- -- /* If any of the original typed text has been changed we need to fix -- * the redo buffer. */ -- ins_compl_fixRedoBufForLeader(NULL); -- -- /* Always add completion for the original text. */ -- vim_free(compl_orig_text); -- compl_orig_text = vim_strnsave(line + 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) -- { -- VIM_CLEAR(compl_pattern); -- VIM_CLEAR(compl_orig_text); -- return FAIL; -- } -- -- /* showmode might reset the internal line pointers, so it must -- * be called before line = ml_get(), or when this address is no -- * longer needed. -- Acevedo. -- */ -- edit_submode_extra = (char_u *)_("-- Searching..."); -- edit_submode_highl = HLF_COUNT; -- showmode(); -- edit_submode_extra = NULL; -- out_flush(); -- } -- else if (insert_match && stop_arrow() == FAIL) -- return FAIL; -- -- compl_shown_match = compl_curr_match; -- compl_shows_dir = compl_direction; -- -- /* -- * Find next match (and following matches). -- */ -- save_w_wrow = curwin->w_wrow; -- save_w_leftcol = curwin->w_leftcol; -- n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE); -- -- /* may undisplay the popup menu */ -- ins_compl_upd_pum(); -- -- if (n > 1) /* all matches have been found */ -- compl_matches = n; -- compl_curr_match = compl_shown_match; -- compl_direction = compl_shows_dir; -- -- /* Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert -- * mode. */ -- if (got_int && !global_busy) -- { -- (void)vgetc(); -- got_int = FALSE; -- } -- -- /* we found no match if the list has only the "compl_orig_text"-entry */ -- if (compl_first_match == compl_first_match->cp_next) -- { -- edit_submode_extra = (compl_cont_status & CONT_ADDING) -- && compl_length > 1 -- ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); -- edit_submode_highl = HLF_E; -- /* remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, -- * because we couldn't expand anything at first place, but if we used -- * ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word -- * (such as M in M'exico) if not tried already. -- Acevedo */ -- if ( compl_length > 1 -- || (compl_cont_status & CONT_ADDING) -- || (ctrl_x_mode != CTRL_X_NORMAL -- && ctrl_x_mode != CTRL_X_PATH_PATTERNS -- && ctrl_x_mode != CTRL_X_PATH_DEFINES)) -- compl_cont_status &= ~CONT_N_ADDS; -- } -- -- if (compl_curr_match->cp_flags & CONT_S_IPOS) -- compl_cont_status |= CONT_S_IPOS; -- else -- compl_cont_status &= ~CONT_S_IPOS; -- -- if (edit_submode_extra == NULL) -- { -- if (compl_curr_match->cp_flags & ORIGINAL_TEXT) -- { -- edit_submode_extra = (char_u *)_("Back at original"); -- edit_submode_highl = HLF_W; -- } -- else if (compl_cont_status & CONT_S_IPOS) -- { -- edit_submode_extra = (char_u *)_("Word from other line"); -- edit_submode_highl = HLF_COUNT; -- } -- else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) -- { -- edit_submode_extra = (char_u *)_("The only match"); -- edit_submode_highl = HLF_COUNT; -- } -- else -- { -- /* Update completion sequence number when needed. */ -- if (compl_curr_match->cp_number == -1) -- { -- int number = 0; -- compl_T *match; -- -- if (compl_direction == FORWARD) -- { -- /* search backwards for the first valid (!= -1) number. -- * This should normally succeed already at the first loop -- * cycle, so it's fast! */ -- for (match = compl_curr_match->cp_prev; match != NULL -- && match != compl_first_match; -- match = match->cp_prev) -- if (match->cp_number != -1) -- { -- number = match->cp_number; -- break; -- } -- if (match != NULL) -- /* go up and assign all numbers which are not assigned -- * yet */ -- for (match = match->cp_next; -- match != NULL && match->cp_number == -1; -- match = match->cp_next) -- match->cp_number = ++number; -- } -- else /* BACKWARD */ -- { -- /* search forwards (upwards) for the first valid (!= -1) -- * number. This should normally succeed already at the -- * first loop cycle, so it's fast! */ -- for (match = compl_curr_match->cp_next; match != NULL -- && match != compl_first_match; -- match = match->cp_next) -- if (match->cp_number != -1) -- { -- number = match->cp_number; -- break; -- } -- if (match != NULL) -- /* go down and assign all numbers which are not -- * assigned yet */ -- for (match = match->cp_prev; match -- && match->cp_number == -1; -- match = match->cp_prev) -- match->cp_number = ++number; -- } -- } -- -- /* The match should always have a sequence number now, this is -- * just a safety check. */ -- if (compl_curr_match->cp_number != -1) -- { -- /* Space for 10 text chars. + 2x10-digit no.s = 31. -- * Translations may need more than twice that. */ -- static char_u match_ref[81]; -- -- if (compl_matches > 0) -- vim_snprintf((char *)match_ref, sizeof(match_ref), -- _("match %d of %d"), -- compl_curr_match->cp_number, compl_matches); -- else -- vim_snprintf((char *)match_ref, sizeof(match_ref), -- _("match %d"), -- compl_curr_match->cp_number); -- edit_submode_extra = match_ref; -- edit_submode_highl = HLF_R; -- if (dollar_vcol >= 0) -- curs_columns(FALSE); -- } -- } -- } -- -- // Show a message about what (completion) mode we're in. -- if (!compl_opt_suppress_empty) -- { -- showmode(); -- if (!shortmess(SHM_COMPLETIONMENU)) -- { -- if (edit_submode_extra != NULL) -- { -- if (!p_smd) -- msg_attr((char *)edit_submode_extra, -- edit_submode_highl < HLF_COUNT -- ? HL_ATTR(edit_submode_highl) : 0); -- } -- else -- msg_clr_cmdline(); // necessary for "noshowmode" -- } -- } -- -- /* Show the popup menu, unless we got interrupted. */ -- if (enable_pum && !compl_interrupted) -- show_pum(save_w_wrow, save_w_leftcol); -- -- compl_was_interrupted = compl_interrupted; -- compl_interrupted = FALSE; -- -- return OK; -- } -- -- static void -- show_pum(int prev_w_wrow, int prev_w_leftcol) -- { -- /* RedrawingDisabled may be set when invoked through complete(). */ -- int n = RedrawingDisabled; -- -- RedrawingDisabled = 0; -- -- /* If the cursor moved or the display scrolled we need to remove the pum -- * first. */ -- setcursor(); -- if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol) -- ins_compl_del_pum(); -- -- ins_compl_show_pum(); -- setcursor(); -- RedrawingDisabled = n; -- } -- -- /* -- * Looks in the first "len" chars. of "src" for search-metachars. -- * If dest is not NULL the chars. are copied there quoting (with -- * a backslash) the metachars, and dest would be NUL terminated. -- * Returns the length (needed) of dest -- */ -- static unsigned -- quote_meta(char_u *dest, char_u *src, int len) -- { -- unsigned m = (unsigned)len + 1; /* one extra for the NUL */ -- -- for ( ; --len >= 0; src++) -- { -- switch (*src) -- { -- case '.': -- case '*': -- case '[': -- if (ctrl_x_mode == CTRL_X_DICTIONARY -- || ctrl_x_mode == CTRL_X_THESAURUS) -- break; -- /* FALLTHROUGH */ -- case '~': -- if (!p_magic) /* quote these only if magic is set */ -- break; -- /* FALLTHROUGH */ -- case '\\': -- if (ctrl_x_mode == CTRL_X_DICTIONARY -- || ctrl_x_mode == CTRL_X_THESAURUS) -- break; -- /* FALLTHROUGH */ -- case '^': /* currently it's not needed. */ -- case '$': -- m++; -- if (dest != NULL) -- *dest++ = '\\'; -- break; -- } -- if (dest != NULL) -- *dest++ = *src; -- /* Copy remaining bytes of a multibyte character. */ -- if (has_mbyte) -- { -- int i, mb_len; -- -- mb_len = (*mb_ptr2len)(src) - 1; -- if (mb_len > 0 && len >= mb_len) -- for (i = 0; i < mb_len; ++i) -- { -- --len; -- ++src; -- if (dest != NULL) -- *dest++ = *src; -- } -- } -- } -- if (dest != NULL) -- *dest = NUL; -- -- return m; -- } -- #endif /* FEAT_INS_EXPAND */ -- - /* - * Next character is interpreted literally. - * A one, two or three digit decimal number is interpreted as its byte value. ---- 2120,2125 ---- -*************** -*** 7054,7060 **** - * start_arrow() is called when an arrow key is used in insert mode. - * For undo/redo it resembles hitting the <ESC> key. - */ -! static void - start_arrow( - pos_T *end_insert_pos) /* can be NULL */ - { ---- 3148,3154 ---- - * start_arrow() is called when an arrow key is used in insert mode. - * For undo/redo it resembles hitting the <ESC> key. - */ -! void - start_arrow( - pos_T *end_insert_pos) /* can be NULL */ - { -*************** -*** 7111,7129 **** - } - } - -- /* -- * Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly -- * spelled word, if there is one. -- */ -- static void -- spell_back_to_badword(void) -- { -- pos_T tpos = curwin->w_cursor; -- -- spell_bad_len = spell_move_to(curwin, BACKWARD, TRUE, TRUE, NULL); -- if (curwin->w_cursor.col != tpos.col) -- start_arrow(&tpos); -- } - #endif - - /* ---- 3205,3210 ---- -*************** -*** 7340,7348 **** - free_last_insert(void) - { - VIM_CLEAR(last_insert); -- # ifdef FEAT_INS_EXPAND -- VIM_CLEAR(compl_orig_text); -- # endif - } - #endif - ---- 3421,3426 ---- -*************** -*** 9978,9984 **** - * Handle CR or NL in insert mode. - * Return FAIL when out of memory or can't undo. - */ -! static int - ins_eol(int c) - { - int i; ---- 6056,6062 ---- - * Handle CR or NL in insert mode. - * Return FAIL when out of memory or can't undo. - */ -! int - ins_eol(int c) - { - int i; -*************** -*** 10174,10180 **** - int c = tc; - - #ifdef FEAT_INS_EXPAND -! if (ctrl_x_mode == CTRL_X_SCROLL) - { - if (c == Ctrl_Y) - scrolldown_clamp(); ---- 6252,6258 ---- - int c = tc; - - #ifdef FEAT_INS_EXPAND -! if (ctrl_x_mode_scroll()) - { - if (c == Ctrl_Y) - scrolldown_clamp(); -*************** -*** 10370,10379 **** - } - #endif - - /* - * Trigger "event" and take care of fixing undo. - */ -! static int - ins_apply_autocmds(event_T event) - { - varnumber_T tick = CHANGEDTICK(curbuf); ---- 6448,6465 ---- - } - #endif - -+ #if defined(FEAT_CINDENT) || defined(PROTO) -+ int -+ can_cindent_get(void) -+ { -+ return can_cindent; -+ } -+ #endif -+ - /* - * Trigger "event" and take care of fixing undo. - */ -! int - ins_apply_autocmds(event_T event) - { - varnumber_T tick = CHANGEDTICK(curbuf); -*** ../vim-8.1.1075/src/evalfunc.c 2019-03-29 14:16:34.142861770 +0100 ---- src/evalfunc.c 2019-03-30 12:54:54.305407871 +0100 -*************** -*** 2603,2609 **** - - RedrawingDisabled = 0; - ins_compl_check_keys(0, TRUE); -! rettv->vval.v_number = compl_interrupted; - RedrawingDisabled = saved; - } - ---- 2603,2609 ---- - - RedrawingDisabled = 0; - ins_compl_check_keys(0, TRUE); -! rettv->vval.v_number = ins_compl_interrupted(); - RedrawingDisabled = saved; - } - -*** ../vim-8.1.1075/src/globals.h 2019-03-22 16:33:03.483016118 +0100 ---- src/globals.h 2019-03-30 12:54:54.305407871 +0100 -*************** -*** 119,128 **** - * by the match.) */ - EXTERN int compl_length INIT(= 0); - -- /* Set when character typed while looking for matches and it means we should -- * stop looking for matches. */ -- EXTERN int compl_interrupted INIT(= FALSE); -- - /* List of flags for method of completion. */ - EXTERN int compl_cont_status INIT(= 0); - # define CONT_ADDING 1 /* "normal" or "adding" expansion */ ---- 119,124 ---- -*** ../vim-8.1.1075/src/insexpand.c 2019-03-30 13:32:12.890308674 +0100 ---- src/insexpand.c 2019-03-30 13:48:00.960469588 +0100 -*************** -*** 0 **** ---- 1,3992 ---- -+ /* vi:set ts=8 sts=4 sw=4 noet: -+ * -+ * VIM - Vi IMproved by Bram Moolenaar -+ * -+ * Do ":help uganda" in Vim to read copying and usage conditions. -+ * Do ":help credits" in Vim to see a list of people who contributed. -+ * See README.txt for an overview of the Vim source code. -+ */ -+ -+ /* -+ * insexpand.c: functions for Insert mode completion -+ */ -+ -+ #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[] below. -+ */ -+ # 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] -+ -+ // 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" -+ }; -+ -+ /* -+ * Array indexes used for cp_text[]. -+ */ -+ #define CPT_ABBR 0 // "abbr" -+ #define CPT_MENU 1 // "menu" -+ #define CPT_KIND 2 // "kind" -+ #define CPT_INFO 3 // "info" -+ #define CPT_USER_DATA 4 // "user data" -+ #define CPT_COUNT 5 // Number of entries -+ -+ /* -+ * 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) -+ -+ 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 -+ -+ /* -+ * 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 -+ -+ // Selected one of the matches. When FALSE the match was edited or using the -+ // longest common string. -+ static int compl_used_match; -+ -+ // didn't finish finding completions. -+ static int compl_was_interrupted = FALSE; -+ -+ // Set when character typed while looking for matches and it means we should -+ // stop looking for matches. -+ static int compl_interrupted = FALSE; -+ -+ 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; -+ -+ 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 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 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 char_u *ins_compl_mode(void); -+ static int ins_compl_need_restart(void); -+ static void ins_compl_new_leader(void); -+ 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_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 int ins_compl_key2dir(int c); -+ static int ins_compl_pum_key(int c); -+ static int ins_compl_key2count(int c); -+ static void show_pum(int prev_w_wrow, int prev_w_leftcol); -+ static unsigned quote_meta(char_u *dest, char_u *str, int len); -+ #endif // FEAT_INS_EXPAND -+ -+ #ifdef FEAT_SPELL -+ static void spell_back_to_badword(void); -+ static int spell_bad_len = 0; // length of located bad word -+ #endif -+ -+ #if defined(FEAT_INS_EXPAND) || defined(PROTO) -+ /* -+ * CTRL-X pressed in Insert mode. -+ */ -+ 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(); -+ } -+ } -+ -+ /* -+ * Functions to check the current CTRL-X mode. -+ */ -+ int ctrl_x_mode_none(void) { return ctrl_x_mode == 0; } -+ int ctrl_x_mode_normal(void) { return ctrl_x_mode == CTRL_X_NORMAL; } -+ int ctrl_x_mode_scroll(void) { return ctrl_x_mode == CTRL_X_SCROLL; } -+ int ctrl_x_mode_whole_line(void) { return ctrl_x_mode == CTRL_X_WHOLE_LINE; } -+ int ctrl_x_mode_files(void) { return ctrl_x_mode == CTRL_X_FILES; } -+ int ctrl_x_mode_tags(void) { return ctrl_x_mode == CTRL_X_TAGS; } -+ int ctrl_x_mode_path_patterns(void) { -+ return ctrl_x_mode == CTRL_X_PATH_PATTERNS; } -+ int ctrl_x_mode_path_defines(void) { -+ return ctrl_x_mode == CTRL_X_PATH_DEFINES; } -+ int ctrl_x_mode_dictionary(void) { return ctrl_x_mode == CTRL_X_DICTIONARY; } -+ int ctrl_x_mode_thesaurus(void) { return ctrl_x_mode == CTRL_X_THESAURUS; } -+ int ctrl_x_mode_cmdline(void) { return ctrl_x_mode == CTRL_X_CMDLINE; } -+ int ctrl_x_mode_function(void) { return ctrl_x_mode == CTRL_X_FUNCTION; } -+ int ctrl_x_mode_omni(void) { return ctrl_x_mode == CTRL_X_OMNI; } -+ int ctrl_x_mode_spell(void) { return ctrl_x_mode == CTRL_X_SPELL; } -+ int ctrl_x_mode_line_or_eval(void) { -+ return ctrl_x_mode == CTRL_X_WHOLE_LINE || ctrl_x_mode == CTRL_X_EVAL; } -+ -+ /* -+ * 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. -+ */ -+ 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. -+ */ -+ 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; -+ } -+ -+ /* -+ * Return whether there currently is a shown match. -+ */ -+ int -+ ins_compl_has_shown_match(void) -+ { -+ return compl_shown_match == NULL -+ || compl_shown_match != compl_shown_match->cp_next; -+ } -+ -+ /* -+ * Return whether the shown match is long enough. -+ */ -+ int -+ ins_compl_long_shown_match(void) -+ { -+ return (int)STRLEN(compl_shown_match->cp_str) -+ > curwin->w_cursor.col - compl_col; -+ } -+ -+ /* -+ * 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. -+ */ -+ 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; -+ } -+ -+ /* -+ * Return TRUE if there are two or more matches to be shown in the popup menu. -+ * One if 'completopt' contains "menuone". -+ */ -+ static int -+ pum_enough_matches(void) -+ { -+ compl_T *compl; -+ int i; -+ -+ // Don't display the popup menu if there are no matches or there is only -+ // one (ignoring the original text). -+ compl = compl_first_match; -+ i = 0; -+ do -+ { -+ if (compl == NULL -+ || ((compl->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) -+ break; -+ compl = compl->cp_next; -+ } while (compl != compl_first_match); -+ -+ if (strstr((char *)p_cot, "menuone") != NULL) -+ return (i >= 1); -+ return (i >= 2); -+ } -+ -+ /* -+ * Show the popup menu for the list of matches. -+ * Also adjusts "compl_shown_match" to an entry that is actually displayed. -+ */ -+ void -+ ins_compl_show_pum(void) -+ { -+ compl_T *compl; -+ compl_T *shown_compl = NULL; -+ int did_find_shown_match = FALSE; -+ int shown_match_ok = FALSE; -+ int i; -+ int cur = -1; -+ colnr_T col; -+ int lead_len = 0; -+ -+ if (!pum_wanted() || !pum_enough_matches()) -+ return; -+ -+ #if defined(FEAT_EVAL) -+ // Dirty hard-coded hack: remove any matchparen highlighting. -+ do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|3match none|endif"); -+ #endif -+ -+ // Update the screen later, before drawing the popup menu over it. -+ pum_call_update_screen(); -+ -+ if (compl_match_array == NULL) -+ { -+ // Need to build the popup menu list. -+ compl_match_arraysize = 0; -+ compl = compl_first_match; -+ if (compl_leader != NULL) -+ lead_len = (int)STRLEN(compl_leader); -+ do -+ { -+ if ((compl->cp_flags & ORIGINAL_TEXT) == 0 -+ && (compl_leader == NULL -+ || ins_compl_equal(compl, compl_leader, lead_len))) -+ ++compl_match_arraysize; -+ compl = compl->cp_next; -+ } while (compl != NULL && compl != compl_first_match); -+ if (compl_match_arraysize == 0) -+ return; -+ compl_match_array = (pumitem_T *)alloc_clear( -+ (unsigned)(sizeof(pumitem_T) -+ * compl_match_arraysize)); -+ if (compl_match_array != NULL) -+ { -+ // If the current match is the original text don't find the first -+ // match after it, don't highlight anything. -+ if (compl_shown_match->cp_flags & ORIGINAL_TEXT) -+ shown_match_ok = TRUE; -+ -+ i = 0; -+ compl = compl_first_match; -+ do -+ { -+ if ((compl->cp_flags & ORIGINAL_TEXT) == 0 -+ && (compl_leader == NULL -+ || ins_compl_equal(compl, compl_leader, lead_len))) -+ { -+ if (!shown_match_ok) -+ { -+ if (compl == compl_shown_match || did_find_shown_match) -+ { -+ // This item is the shown match or this is the -+ // first displayed item after the shown match. -+ compl_shown_match = compl; -+ did_find_shown_match = TRUE; -+ shown_match_ok = TRUE; -+ } -+ else -+ // Remember this displayed match for when the -+ // shown match is just below it. -+ shown_compl = compl; -+ cur = i; -+ } -+ -+ if (compl->cp_text[CPT_ABBR] != NULL) -+ compl_match_array[i].pum_text = -+ compl->cp_text[CPT_ABBR]; -+ else -+ compl_match_array[i].pum_text = compl->cp_str; -+ compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; -+ compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; -+ if (compl->cp_text[CPT_MENU] != NULL) -+ compl_match_array[i++].pum_extra = -+ compl->cp_text[CPT_MENU]; -+ else -+ compl_match_array[i++].pum_extra = compl->cp_fname; -+ } -+ -+ if (compl == compl_shown_match) -+ { -+ did_find_shown_match = TRUE; -+ -+ // When the original text is the shown match don't set -+ // compl_shown_match. -+ if (compl->cp_flags & ORIGINAL_TEXT) -+ shown_match_ok = TRUE; -+ -+ if (!shown_match_ok && shown_compl != NULL) -+ { -+ // The shown match isn't displayed, set it to the -+ // previously displayed match. -+ compl_shown_match = shown_compl; -+ shown_match_ok = TRUE; -+ } -+ } -+ compl = compl->cp_next; -+ } while (compl != NULL && compl != compl_first_match); -+ -+ if (!shown_match_ok) // no displayed match at all -+ cur = -1; -+ } -+ } -+ else -+ { -+ // popup menu already exists, only need to find the current item. -+ for (i = 0; i < compl_match_arraysize; ++i) -+ if (compl_match_array[i].pum_text == compl_shown_match->cp_str -+ || compl_match_array[i].pum_text -+ == compl_shown_match->cp_text[CPT_ABBR]) -+ { -+ cur = i; -+ break; -+ } -+ } -+ -+ if (compl_match_array != NULL) -+ { -+ // In Replace mode when a $ is displayed at the end of the line only -+ // part of the screen would be updated. We do need to redraw here. -+ dollar_vcol = -1; -+ -+ // Compute the screen column of the start of the completed text. -+ // Use the cursor to get all wrapping and other settings right. -+ col = curwin->w_cursor.col; -+ curwin->w_cursor.col = compl_col; -+ pum_display(compl_match_array, compl_match_arraysize, cur); -+ curwin->w_cursor.col = col; -+ } -+ } -+ -+ #define DICT_FIRST (1) // use just first element in "dict" -+ #define DICT_EXACT (2) // "dict" is the exact name of a file -+ -+ /* -+ * Add any identifiers that match the given pattern in the list of dictionary -+ * files "dict_start" to the list of completions. -+ */ -+ static void -+ ins_compl_dictionaries( -+ char_u *dict_start, -+ char_u *pat, -+ int flags, // DICT_FIRST and/or DICT_EXACT -+ int thesaurus) // Thesaurus completion -+ { -+ char_u *dict = dict_start; -+ char_u *ptr; -+ char_u *buf; -+ regmatch_T regmatch; -+ char_u **files; -+ int count; -+ int save_p_scs; -+ int dir = compl_direction; -+ -+ if (*dict == NUL) -+ { -+ #ifdef FEAT_SPELL -+ // When 'dictionary' is empty and spell checking is enabled use -+ // "spell". -+ if (!thesaurus && curwin->w_p_spell) -+ dict = (char_u *)"spell"; -+ else -+ #endif -+ return; -+ } -+ -+ buf = alloc(LSIZE); -+ if (buf == NULL) -+ return; -+ regmatch.regprog = NULL; // so that we can goto theend -+ -+ // If 'infercase' is set, don't use 'smartcase' here -+ save_p_scs = p_scs; -+ if (curbuf->b_p_inf) -+ p_scs = FALSE; -+ -+ // When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern -+ // to only match at the start of a line. Otherwise just match the -+ // pattern. Also need to double backslashes. -+ if (ctrl_x_mode_line_or_eval()) -+ { -+ char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); -+ size_t len; -+ -+ if (pat_esc == NULL) -+ goto theend; -+ len = STRLEN(pat_esc) + 10; -+ ptr = alloc((unsigned)len); -+ if (ptr == NULL) -+ { -+ vim_free(pat_esc); -+ goto theend; -+ } -+ vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); -+ regmatch.regprog = vim_regcomp(ptr, RE_MAGIC); -+ vim_free(pat_esc); -+ vim_free(ptr); -+ } -+ else -+ { -+ regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); -+ if (regmatch.regprog == NULL) -+ goto theend; -+ } -+ -+ // ignore case depends on 'ignorecase', 'smartcase' and "pat" -+ regmatch.rm_ic = ignorecase(pat); -+ while (*dict != NUL && !got_int && !compl_interrupted) -+ { -+ // copy one dictionary file name into buf -+ if (flags == DICT_EXACT) -+ { -+ count = 1; -+ files = &dict; -+ } -+ else -+ { -+ // Expand wildcards in the dictionary name, but do not allow -+ // backticks (for security, the 'dict' option may have been set in -+ // a modeline). -+ copy_option_part(&dict, buf, LSIZE, ","); -+ # ifdef FEAT_SPELL -+ if (!thesaurus && STRCMP(buf, "spell") == 0) -+ count = -1; -+ else -+ # endif -+ if (vim_strchr(buf, '`') != NULL -+ || expand_wildcards(1, &buf, &count, &files, -+ EW_FILE|EW_SILENT) != OK) -+ count = 0; -+ } -+ -+ # ifdef FEAT_SPELL -+ if (count == -1) -+ { -+ // Complete from active spelling. Skip "\<" in the pattern, we -+ // don't use it as a RE. -+ if (pat[0] == '\\' && pat[1] == '<') -+ ptr = pat + 2; -+ else -+ ptr = pat; -+ spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); -+ } -+ else -+ # endif -+ if (count > 0) // avoid warning for using "files" uninit -+ { -+ ins_compl_files(count, files, thesaurus, flags, -+ ®match, buf, &dir); -+ if (flags != DICT_EXACT) -+ FreeWild(count, files); -+ } -+ if (flags != 0) -+ break; -+ } -+ -+ theend: -+ p_scs = save_p_scs; -+ vim_regfree(regmatch.regprog); -+ vim_free(buf); -+ } -+ -+ static void -+ ins_compl_files( -+ int count, -+ char_u **files, -+ int thesaurus, -+ int flags, -+ regmatch_T *regmatch, -+ char_u *buf, -+ int *dir) -+ { -+ char_u *ptr; -+ int i; -+ FILE *fp; -+ int add_r; -+ -+ for (i = 0; i < count && !got_int && !compl_interrupted; i++) -+ { -+ fp = mch_fopen((char *)files[i], "r"); // open dictionary file -+ if (flags != DICT_EXACT) -+ { -+ vim_snprintf((char *)IObuff, IOSIZE, -+ _("Scanning dictionary: %s"), (char *)files[i]); -+ (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); -+ } -+ -+ if (fp != NULL) -+ { -+ // Read dictionary file line by line. -+ // Check each line for a match. -+ while (!got_int && !compl_interrupted -+ && !vim_fgets(buf, LSIZE, fp)) -+ { -+ ptr = buf; -+ while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) -+ { -+ ptr = regmatch->startp[0]; -+ if (ctrl_x_mode_line_or_eval()) -+ ptr = find_line_end(ptr); -+ else -+ ptr = find_word_end(ptr); -+ add_r = ins_compl_add_infercase(regmatch->startp[0], -+ (int)(ptr - regmatch->startp[0]), -+ p_ic, files[i], *dir, 0); -+ if (thesaurus) -+ { -+ char_u *wstart; -+ -+ // Add the other matches on the line -+ ptr = buf; -+ while (!got_int) -+ { -+ // Find start of the next word. Skip white -+ // space and punctuation. -+ ptr = find_word_start(ptr); -+ if (*ptr == NUL || *ptr == NL) -+ break; -+ wstart = ptr; -+ -+ // Find end of the word. -+ if (has_mbyte) -+ // Japanese words may have characters in -+ // different classes, only separate words -+ // with single-byte non-word characters. -+ while (*ptr != NUL) -+ { -+ int l = (*mb_ptr2len)(ptr); -+ -+ if (l < 2 && !vim_iswordc(*ptr)) -+ break; -+ ptr += l; -+ } -+ else -+ ptr = find_word_end(ptr); -+ -+ // Add the word. Skip the regexp match. -+ if (wstart != regmatch->startp[0]) -+ add_r = ins_compl_add_infercase(wstart, -+ (int)(ptr - wstart), -+ p_ic, files[i], *dir, 0); -+ } -+ } -+ if (add_r == OK) -+ // if dir was BACKWARD then honor it just once -+ *dir = FORWARD; -+ else if (add_r == FAIL) -+ break; -+ // avoid expensive call to vim_regexec() when at end -+ // of line -+ if (*ptr == '\n' || got_int) -+ break; -+ } -+ line_breakcheck(); -+ ins_compl_check_keys(50, FALSE); -+ } -+ fclose(fp); -+ } -+ } -+ } -+ -+ /* -+ * Find the start of the next word. -+ * Returns a pointer to the first char of the word. Also stops at a NUL. -+ */ -+ char_u * -+ find_word_start(char_u *ptr) -+ { -+ if (has_mbyte) -+ while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) -+ ptr += (*mb_ptr2len)(ptr); -+ else -+ while (*ptr != NUL && *ptr != '\n' && !vim_iswordc(*ptr)) -+ ++ptr; -+ return ptr; -+ } -+ -+ /* -+ * Find the end of the word. Assumes it starts inside a word. -+ * Returns a pointer to just after the word. -+ */ -+ char_u * -+ find_word_end(char_u *ptr) -+ { -+ int start_class; -+ -+ if (has_mbyte) -+ { -+ start_class = mb_get_class(ptr); -+ if (start_class > 1) -+ while (*ptr != NUL) -+ { -+ ptr += (*mb_ptr2len)(ptr); -+ if (mb_get_class(ptr) != start_class) -+ break; -+ } -+ } -+ else -+ while (vim_iswordc(*ptr)) -+ ++ptr; -+ return ptr; -+ } -+ -+ /* -+ * Find the end of the line, omitting CR and NL at the end. -+ * Returns a pointer to just after the line. -+ */ -+ static char_u * -+ find_line_end(char_u *ptr) -+ { -+ char_u *s; -+ -+ s = ptr + STRLEN(ptr); -+ while (s > ptr && (s[-1] == CAR || s[-1] == NL)) -+ --s; -+ return s; -+ } -+ -+ /* -+ * Free the list of completions -+ */ -+ static void -+ ins_compl_free(void) -+ { -+ compl_T *match; -+ int i; -+ -+ VIM_CLEAR(compl_pattern); -+ VIM_CLEAR(compl_leader); -+ -+ if (compl_first_match == NULL) -+ return; -+ -+ ins_compl_del_pum(); -+ pum_clear(); -+ -+ compl_curr_match = compl_first_match; -+ do -+ { -+ match = compl_curr_match; -+ compl_curr_match = compl_curr_match->cp_next; -+ vim_free(match->cp_str); -+ // several entries may use the same fname, free it just once. -+ if (match->cp_flags & FREE_FNAME) -+ vim_free(match->cp_fname); -+ for (i = 0; i < CPT_COUNT; ++i) -+ vim_free(match->cp_text[i]); -+ vim_free(match); -+ } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); -+ compl_first_match = compl_curr_match = NULL; -+ compl_shown_match = NULL; -+ compl_old_match = NULL; -+ } -+ -+ void -+ ins_compl_clear(void) -+ { -+ compl_cont_status = 0; -+ compl_started = FALSE; -+ compl_matches = 0; -+ VIM_CLEAR(compl_pattern); -+ VIM_CLEAR(compl_leader); -+ edit_submode_extra = NULL; -+ VIM_CLEAR(compl_orig_text); -+ compl_enter_selects = FALSE; -+ // clear v:completed_item -+ set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); -+ } -+ -+ /* -+ * Return TRUE when Insert completion is active. -+ */ -+ int -+ ins_compl_active(void) -+ { -+ return compl_started; -+ } -+ -+ /* -+ * Get complete information -+ */ -+ void -+ get_complete_info(list_T *what_list, dict_T *retdict) -+ { -+ int ret = OK; -+ listitem_T *item; -+ #define CI_WHAT_MODE 0x01 -+ #define CI_WHAT_PUM_VISIBLE 0x02 -+ #define CI_WHAT_ITEMS 0x04 -+ #define CI_WHAT_SELECTED 0x08 -+ #define CI_WHAT_INSERTED 0x10 -+ #define CI_WHAT_ALL 0xff -+ int what_flag; -+ -+ if (what_list == NULL) -+ what_flag = CI_WHAT_ALL; -+ else -+ { -+ what_flag = 0; -+ for (item = what_list->lv_first; item != NULL; item = item->li_next) -+ { -+ char_u *what = tv_get_string(&item->li_tv); -+ -+ if (STRCMP(what, "mode") == 0) -+ what_flag |= CI_WHAT_MODE; -+ else if (STRCMP(what, "pum_visible") == 0) -+ what_flag |= CI_WHAT_PUM_VISIBLE; -+ else if (STRCMP(what, "items") == 0) -+ what_flag |= CI_WHAT_ITEMS; -+ else if (STRCMP(what, "selected") == 0) -+ what_flag |= CI_WHAT_SELECTED; -+ else if (STRCMP(what, "inserted") == 0) -+ what_flag |= CI_WHAT_INSERTED; -+ } -+ } -+ -+ if (ret == OK && (what_flag & CI_WHAT_MODE)) -+ ret = dict_add_string(retdict, "mode", ins_compl_mode()); -+ -+ if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) -+ ret = dict_add_number(retdict, "pum_visible", pum_visible()); -+ -+ if (ret == OK && (what_flag & CI_WHAT_ITEMS)) -+ { -+ list_T *li; -+ dict_T *di; -+ compl_T *match; -+ -+ li = list_alloc(); -+ if (li == NULL) -+ return; -+ ret = dict_add_list(retdict, "items", li); -+ if (ret == OK && compl_first_match != NULL) -+ { -+ match = compl_first_match; -+ do -+ { -+ if (!(match->cp_flags & ORIGINAL_TEXT)) -+ { -+ di = dict_alloc(); -+ if (di == NULL) -+ return; -+ ret = list_append_dict(li, di); -+ if (ret != OK) -+ return; -+ dict_add_string(di, "word", match->cp_str); -+ dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]); -+ dict_add_string(di, "menu", match->cp_text[CPT_MENU]); -+ dict_add_string(di, "kind", match->cp_text[CPT_KIND]); -+ dict_add_string(di, "info", match->cp_text[CPT_INFO]); -+ dict_add_string(di, "user_data", -+ match->cp_text[CPT_USER_DATA]); -+ } -+ match = match->cp_next; -+ } -+ while (match != NULL && match != compl_first_match); -+ } -+ } -+ -+ if (ret == OK && (what_flag & CI_WHAT_SELECTED)) -+ ret = dict_add_number(retdict, "selected", (compl_curr_match != NULL) ? -+ compl_curr_match->cp_number - 1 : -1); -+ -+ // TODO -+ // if (ret == OK && (what_flag & CI_WHAT_INSERTED)) -+ } -+ -+ /* -+ * Return Insert completion mode name string -+ */ -+ static char_u * -+ ins_compl_mode(void) -+ { -+ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || compl_started) -+ return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; -+ -+ return (char_u *)""; -+ } -+ -+ /* -+ * Selected one of the matches. When FALSE the match was edited or using the -+ * longest common string. -+ */ -+ int -+ ins_compl_used_match(void) -+ { -+ return compl_used_match; -+ } -+ -+ /* -+ * Initialize get longest common string. -+ */ -+ void -+ ins_compl_init_get_longest(void) -+ { -+ compl_get_longest = FALSE; -+ } -+ -+ /* -+ * Returns TRUE when insert completion is interrupted. -+ */ -+ int -+ ins_compl_interrupted(void) -+ { -+ return compl_interrupted; -+ } -+ -+ /* -+ * Returns TRUE if the <Enter> key selects a match in the completion popup -+ * menu. -+ */ -+ int -+ ins_compl_enter_selects(void) -+ { -+ return compl_enter_selects; -+ } -+ -+ /* -+ * Return the column where the text starts that is being completed -+ */ -+ colnr_T -+ ins_compl_col(void) -+ { -+ return compl_col; -+ } -+ -+ /* -+ * Delete one character before the cursor and show the subset of the matches -+ * that match the word that is now before the cursor. -+ * Returns the character to be used, NUL if the work is done and another char -+ * to be got from the user. -+ */ -+ int -+ ins_compl_bs(void) -+ { -+ char_u *line; -+ char_u *p; -+ -+ line = ml_get_curline(); -+ p = line + curwin->w_cursor.col; -+ MB_PTR_BACK(line, p); -+ -+ // Stop completion when the whole word was deleted. For Omni completion -+ // allow the word to be deleted, we won't match everything. -+ // Respect the 'backspace' option. -+ if ((int)(p - line) - (int)compl_col < 0 -+ || ((int)(p - line) - (int)compl_col == 0 -+ && ctrl_x_mode != CTRL_X_OMNI) || ctrl_x_mode == CTRL_X_EVAL -+ || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col -+ - compl_length < 0)) -+ return K_BS; -+ -+ // Deleted more than what was used to find matches or didn't finish -+ // finding all matches: need to look for matches all over again. -+ if (curwin->w_cursor.col <= compl_col + compl_length -+ || ins_compl_need_restart()) -+ ins_compl_restart(); -+ -+ vim_free(compl_leader); -+ compl_leader = vim_strnsave(line + compl_col, (int)(p - line) - compl_col); -+ if (compl_leader != NULL) -+ { -+ ins_compl_new_leader(); -+ if (compl_shown_match != NULL) -+ // Make sure current match is not a hidden item. -+ compl_curr_match = compl_shown_match; -+ return NUL; -+ } -+ return K_BS; -+ } -+ -+ /* -+ * Return TRUE when we need to find matches again, ins_compl_restart() is to -+ * be called. -+ */ -+ static int -+ ins_compl_need_restart(void) -+ { -+ // Return TRUE if we didn't complete finding matches or when the -+ // 'completefunc' returned "always" in the "refresh" dictionary item. -+ return compl_was_interrupted -+ || ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) -+ && compl_opt_refresh_always); -+ } -+ -+ /* -+ * Called after changing "compl_leader". -+ * Show the popup menu with a different set of matches. -+ * May also search for matches again if the previous search was interrupted. -+ */ -+ static void -+ ins_compl_new_leader(void) -+ { -+ ins_compl_del_pum(); -+ ins_compl_delete(); -+ ins_bytes(compl_leader + ins_compl_len()); -+ compl_used_match = FALSE; -+ -+ if (compl_started) -+ ins_compl_set_original_text(compl_leader); -+ else -+ { -+ #ifdef FEAT_SPELL -+ spell_bad_len = 0; // need to redetect bad word -+ #endif -+ // Matches were cleared, need to search for them now. Befor drawing -+ // the popup menu display the changed text before the cursor. Set -+ // "compl_restarting" to avoid that the first match is inserted. -+ pum_call_update_screen(); -+ #ifdef FEAT_GUI -+ if (gui.in_use) -+ { -+ // Show the cursor after the match, not after the redrawn text. -+ setcursor(); -+ out_flush_cursor(FALSE, FALSE); -+ } -+ #endif -+ compl_restarting = TRUE; -+ if (ins_complete(Ctrl_N, TRUE) == FAIL) -+ compl_cont_status = 0; -+ compl_restarting = FALSE; -+ } -+ -+ compl_enter_selects = !compl_used_match; -+ -+ // Show the popup menu with a different set of matches. -+ ins_compl_show_pum(); -+ -+ // Don't let Enter select the original text when there is no popup menu. -+ if (compl_match_array == NULL) -+ compl_enter_selects = FALSE; -+ } -+ -+ /* -+ * Return the length of the completion, from the completion start column to -+ * the cursor column. Making sure it never goes below zero. -+ */ -+ static int -+ ins_compl_len(void) -+ { -+ int off = (int)curwin->w_cursor.col - (int)compl_col; -+ -+ if (off < 0) -+ return 0; -+ return off; -+ } -+ -+ /* -+ * Append one character to the match leader. May reduce the number of -+ * matches. -+ */ -+ void -+ ins_compl_addleader(int c) -+ { -+ int cc; -+ -+ if (stop_arrow() == FAIL) -+ return; -+ if (has_mbyte && (cc = (*mb_char2len)(c)) > 1) -+ { -+ char_u buf[MB_MAXBYTES + 1]; -+ -+ (*mb_char2bytes)(c, buf); -+ buf[cc] = NUL; -+ ins_char_bytes(buf, cc); -+ if (compl_opt_refresh_always) -+ AppendToRedobuff(buf); -+ } -+ else -+ { -+ ins_char(c); -+ if (compl_opt_refresh_always) -+ AppendCharToRedobuff(c); -+ } -+ -+ // If we didn't complete finding matches we must search again. -+ if (ins_compl_need_restart()) -+ ins_compl_restart(); -+ -+ // When 'always' is set, don't reset compl_leader. While completing, -+ // cursor doesn't point original position, changing compl_leader would -+ // break redo. -+ if (!compl_opt_refresh_always) -+ { -+ vim_free(compl_leader); -+ compl_leader = vim_strnsave(ml_get_curline() + compl_col, -+ (int)(curwin->w_cursor.col - compl_col)); -+ if (compl_leader != NULL) -+ ins_compl_new_leader(); -+ } -+ } -+ -+ /* -+ * Setup for finding completions again without leaving CTRL-X mode. Used when -+ * BS or a key was typed while still searching for matches. -+ */ -+ static void -+ ins_compl_restart(void) -+ { -+ ins_compl_free(); -+ compl_started = FALSE; -+ compl_matches = 0; -+ compl_cont_status = 0; -+ compl_cont_mode = 0; -+ } -+ -+ /* -+ * Set the first match, the original text. -+ */ -+ static void -+ ins_compl_set_original_text(char_u *str) -+ { -+ char_u *p; -+ -+ // Replace the original text entry. -+ // The ORIGINAL_TEXT flag is either at the first item or might possibly be -+ // at the last item for backward completion -+ if (compl_first_match->cp_flags & ORIGINAL_TEXT) // safety check -+ { -+ p = vim_strsave(str); -+ if (p != NULL) -+ { -+ vim_free(compl_first_match->cp_str); -+ compl_first_match->cp_str = p; -+ } -+ } -+ else if (compl_first_match->cp_prev != NULL -+ && (compl_first_match->cp_prev->cp_flags & ORIGINAL_TEXT)) -+ { -+ p = vim_strsave(str); -+ if (p != NULL) -+ { -+ vim_free(compl_first_match->cp_prev->cp_str); -+ compl_first_match->cp_prev->cp_str = p; -+ } -+ } -+ } -+ -+ /* -+ * Append one character to the match leader. May reduce the number of -+ * matches. -+ */ -+ void -+ ins_compl_addfrommatch(void) -+ { -+ char_u *p; -+ int len = (int)curwin->w_cursor.col - (int)compl_col; -+ int c; -+ compl_T *cp; -+ -+ p = compl_shown_match->cp_str; -+ if ((int)STRLEN(p) <= len) // the match is too short -+ { -+ // When still at the original match use the first entry that matches -+ // the leader. -+ if (compl_shown_match->cp_flags & ORIGINAL_TEXT) -+ { -+ p = NULL; -+ for (cp = compl_shown_match->cp_next; cp != NULL -+ && cp != compl_first_match; cp = cp->cp_next) -+ { -+ if (compl_leader == NULL -+ || ins_compl_equal(cp, compl_leader, -+ (int)STRLEN(compl_leader))) -+ { -+ p = cp->cp_str; -+ break; -+ } -+ } -+ if (p == NULL || (int)STRLEN(p) <= len) -+ return; -+ } -+ else -+ return; -+ } -+ p += len; -+ c = PTR2CHAR(p); -+ ins_compl_addleader(c); -+ } -+ -+ /* -+ * Prepare for Insert mode completion, or stop it. -+ * Called just after typing a character in Insert mode. -+ * Returns TRUE when the character is not to be inserted; -+ */ -+ int -+ ins_compl_prep(int c) -+ { -+ char_u *ptr; -+ int want_cindent; -+ int retval = FALSE; -+ -+ // Forget any previous 'special' messages if this is actually -+ // a ^X mode key - bar ^R, in which case we wait to see what it gives us. -+ if (c != Ctrl_R && vim_is_ctrl_x_key(c)) -+ edit_submode_extra = NULL; -+ -+ // Ignore end of Select mode mapping and mouse scroll buttons. -+ if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP -+ || c == K_MOUSELEFT || c == K_MOUSERIGHT) -+ return retval; -+ -+ // Set "compl_get_longest" when finding the first matches. -+ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET -+ || (ctrl_x_mode == CTRL_X_NORMAL && !compl_started)) -+ { -+ compl_get_longest = (strstr((char *)p_cot, "longest") != NULL); -+ compl_used_match = TRUE; -+ -+ } -+ -+ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) -+ { -+ // We have just typed CTRL-X and aren't quite sure which CTRL-X mode -+ // it will be yet. Now we decide. -+ switch (c) -+ { -+ case Ctrl_E: -+ case Ctrl_Y: -+ ctrl_x_mode = CTRL_X_SCROLL; -+ if (!(State & REPLACE_FLAG)) -+ edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); -+ else -+ edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); -+ edit_submode_pre = NULL; -+ showmode(); -+ break; -+ case Ctrl_L: -+ ctrl_x_mode = CTRL_X_WHOLE_LINE; -+ break; -+ case Ctrl_F: -+ ctrl_x_mode = CTRL_X_FILES; -+ break; -+ case Ctrl_K: -+ ctrl_x_mode = CTRL_X_DICTIONARY; -+ break; -+ case Ctrl_R: -+ // Simply allow ^R to happen without affecting ^X mode -+ break; -+ case Ctrl_T: -+ ctrl_x_mode = CTRL_X_THESAURUS; -+ break; -+ #ifdef FEAT_COMPL_FUNC -+ case Ctrl_U: -+ ctrl_x_mode = CTRL_X_FUNCTION; -+ break; -+ case Ctrl_O: -+ ctrl_x_mode = CTRL_X_OMNI; -+ break; -+ #endif -+ case 's': -+ case Ctrl_S: -+ ctrl_x_mode = CTRL_X_SPELL; -+ #ifdef FEAT_SPELL -+ ++emsg_off; // Avoid getting the E756 error twice. -+ spell_back_to_badword(); -+ --emsg_off; -+ #endif -+ break; -+ case Ctrl_RSB: -+ ctrl_x_mode = CTRL_X_TAGS; -+ break; -+ #ifdef FEAT_FIND_ID -+ case Ctrl_I: -+ case K_S_TAB: -+ ctrl_x_mode = CTRL_X_PATH_PATTERNS; -+ break; -+ case Ctrl_D: -+ ctrl_x_mode = CTRL_X_PATH_DEFINES; -+ break; -+ #endif -+ case Ctrl_V: -+ case Ctrl_Q: -+ ctrl_x_mode = CTRL_X_CMDLINE; -+ break; -+ case Ctrl_P: -+ case Ctrl_N: -+ // ^X^P means LOCAL expansion if nothing interrupted (eg we -+ // just started ^X mode, or there were enough ^X's to cancel -+ // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) -+ // do normal expansion when interrupting a different mode (say -+ // ^X^F^X^P or ^P^X^X^P, see below) -+ // nothing changes if interrupting mode 0, (eg, the flag -+ // doesn't change when going to ADDING mode -- Acevedo -+ if (!(compl_cont_status & CONT_INTRPT)) -+ compl_cont_status |= CONT_LOCAL; -+ else if (compl_cont_mode != 0) -+ compl_cont_status &= ~CONT_LOCAL; -+ // FALLTHROUGH -+ default: -+ // If we have typed at least 2 ^X's... for modes != 0, we set -+ // compl_cont_status = 0 (eg, as if we had just started ^X -+ // mode). -+ // For mode 0, we set "compl_cont_mode" to an impossible -+ // value, in both cases ^X^X can be used to restart the same -+ // mode (avoiding ADDING mode). -+ // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start -+ // 'complete' and local ^P expansions respectively. -+ // In mode 0 an extra ^X is needed since ^X^P goes to ADDING -+ // mode -- Acevedo -+ if (c == Ctrl_X) -+ { -+ if (compl_cont_mode != 0) -+ compl_cont_status = 0; -+ else -+ compl_cont_mode = CTRL_X_NOT_DEFINED_YET; -+ } -+ ctrl_x_mode = CTRL_X_NORMAL; -+ edit_submode = NULL; -+ showmode(); -+ break; -+ } -+ } -+ else if (ctrl_x_mode != CTRL_X_NORMAL) -+ { -+ // We're already in CTRL-X mode, do we stay in it? -+ if (!vim_is_ctrl_x_key(c)) -+ { -+ if (ctrl_x_mode == CTRL_X_SCROLL) -+ ctrl_x_mode = CTRL_X_NORMAL; -+ else -+ ctrl_x_mode = CTRL_X_FINISHED; -+ edit_submode = NULL; -+ } -+ showmode(); -+ } -+ -+ if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) -+ { -+ // Show error message from attempted keyword completion (probably -+ // 'Pattern not found') until another key is hit, then go back to -+ // showing what mode we are in. -+ showmode(); -+ if ((ctrl_x_mode == CTRL_X_NORMAL && c != Ctrl_N && c != Ctrl_P -+ && c != Ctrl_R && !ins_compl_pum_key(c)) -+ || ctrl_x_mode == CTRL_X_FINISHED) -+ { -+ // Get here when we have finished typing a sequence of ^N and -+ // ^P or other completion characters in CTRL-X mode. Free up -+ // memory that was used, and make sure we can redo the insert. -+ if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) -+ { -+ // If any of the original typed text has been changed, eg when -+ // ignorecase is set, we must add back-spaces to the redo -+ // buffer. We add as few as necessary to delete just the part -+ // of the original text that has changed. -+ // When using the longest match, edited the match or used -+ // CTRL-E then don't use the current match. -+ if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) -+ ptr = compl_curr_match->cp_str; -+ else -+ ptr = NULL; -+ ins_compl_fixRedoBufForLeader(ptr); -+ } -+ -+ #ifdef FEAT_CINDENT -+ want_cindent = (can_cindent_get() && cindent_on()); -+ #endif -+ // When completing whole lines: fix indent for 'cindent'. -+ // Otherwise, break line if it's too long. -+ if (compl_cont_mode == CTRL_X_WHOLE_LINE) -+ { -+ #ifdef FEAT_CINDENT -+ // re-indent the current line -+ if (want_cindent) -+ { -+ do_c_expr_indent(); -+ want_cindent = FALSE; // don't do it again -+ } -+ #endif -+ } -+ else -+ { -+ int prev_col = curwin->w_cursor.col; -+ -+ // put the cursor on the last char, for 'tw' formatting -+ if (prev_col > 0) -+ dec_cursor(); -+ // only format when something was inserted -+ if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) -+ insertchar(NUL, 0, -1); -+ if (prev_col > 0 -+ && ml_get_curline()[curwin->w_cursor.col] != NUL) -+ inc_cursor(); -+ } -+ -+ // If the popup menu is displayed pressing CTRL-Y means accepting -+ // the selection without inserting anything. When -+ // compl_enter_selects is set the Enter key does the same. -+ if ((c == Ctrl_Y || (compl_enter_selects -+ && (c == CAR || c == K_KENTER || c == NL))) -+ && pum_visible()) -+ retval = TRUE; -+ -+ // CTRL-E means completion is Ended, go back to the typed text. -+ // but only do this, if the Popup is still visible -+ if (c == Ctrl_E) -+ { -+ ins_compl_delete(); -+ if (compl_leader != NULL) -+ ins_bytes(compl_leader + ins_compl_len()); -+ else if (compl_first_match != NULL) -+ ins_bytes(compl_orig_text + ins_compl_len()); -+ retval = TRUE; -+ } -+ -+ auto_format(FALSE, TRUE); -+ -+ ins_compl_free(); -+ compl_started = FALSE; -+ compl_matches = 0; -+ if (!shortmess(SHM_COMPLETIONMENU)) -+ msg_clr_cmdline(); // necessary for "noshowmode" -+ ctrl_x_mode = CTRL_X_NORMAL; -+ compl_enter_selects = FALSE; -+ if (edit_submode != NULL) -+ { -+ edit_submode = NULL; -+ showmode(); -+ } -+ -+ #ifdef FEAT_CMDWIN -+ if (c == Ctrl_C && cmdwin_type != 0) -+ // Avoid the popup menu remains displayed when leaving the -+ // command line window. -+ update_screen(0); -+ #endif -+ #ifdef FEAT_CINDENT -+ // Indent now if a key was typed that is in 'cinkeys'. -+ if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) -+ do_c_expr_indent(); -+ #endif -+ // Trigger the CompleteDone event to give scripts a chance to act -+ // upon the completion. -+ ins_apply_autocmds(EVENT_COMPLETEDONE); -+ } -+ } -+ else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) -+ // Trigger the CompleteDone event to give scripts a chance to act -+ // upon the (possibly failed) completion. -+ ins_apply_autocmds(EVENT_COMPLETEDONE); -+ -+ // reset continue_* if we left expansion-mode, if we stay they'll be -+ // (re)set properly in ins_complete() -+ if (!vim_is_ctrl_x_key(c)) -+ { -+ compl_cont_status = 0; -+ compl_cont_mode = 0; -+ } -+ -+ return retval; -+ } -+ -+ /* -+ * Fix the redo buffer for the completion leader replacing some of the typed -+ * text. This inserts backspaces and appends the changed text. -+ * "ptr" is the known leader text or NUL. -+ */ -+ static void -+ ins_compl_fixRedoBufForLeader(char_u *ptr_arg) -+ { -+ int len; -+ char_u *p; -+ char_u *ptr = ptr_arg; -+ -+ if (ptr == NULL) -+ { -+ if (compl_leader != NULL) -+ ptr = compl_leader; -+ else -+ return; // nothing to do -+ } -+ if (compl_orig_text != NULL) -+ { -+ p = compl_orig_text; -+ for (len = 0; p[len] != NUL && p[len] == ptr[len]; ++len) -+ ; -+ if (len > 0) -+ len -= (*mb_head_off)(p, p + len); -+ for (p += len; *p != NUL; MB_PTR_ADV(p)) -+ AppendCharToRedobuff(K_BS); -+ } -+ else -+ len = 0; -+ if (ptr != NULL) -+ AppendToRedobuffLit(ptr + len, -1); -+ } -+ -+ /* -+ * Loops through the list of windows, loaded-buffers or non-loaded-buffers -+ * (depending on flag) starting from buf and looking for a non-scanned -+ * buffer (other than curbuf). curbuf is special, if it is called with -+ * buf=curbuf then it has to be the first call for a given flag/expansion. -+ * -+ * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo -+ */ -+ static buf_T * -+ ins_compl_next_buf(buf_T *buf, int flag) -+ { -+ static win_T *wp = NULL; -+ -+ if (flag == 'w') // just windows -+ { -+ if (buf == curbuf || wp == NULL) // first call for this flag/expansion -+ wp = curwin; -+ while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin -+ && wp->w_buffer->b_scanned) -+ ; -+ buf = wp->w_buffer; -+ } -+ else -+ // 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U' -+ // (unlisted buffers) -+ // When completing whole lines skip unloaded buffers. -+ while ((buf = (buf->b_next != NULL ? buf->b_next : firstbuf)) != curbuf -+ && ((flag == 'U' -+ ? buf->b_p_bl -+ : (!buf->b_p_bl -+ || (buf->b_ml.ml_mfp == NULL) != (flag == 'u'))) -+ || buf->b_scanned)) -+ ; -+ return buf; -+ } -+ -+ #ifdef FEAT_COMPL_FUNC -+ /* -+ * Execute user defined complete function 'completefunc' or 'omnifunc', and -+ * get matches in "matches". -+ */ -+ static void -+ expand_by_function( -+ int type, // CTRL_X_OMNI or CTRL_X_FUNCTION -+ char_u *base) -+ { -+ list_T *matchlist = NULL; -+ dict_T *matchdict = NULL; -+ typval_T args[3]; -+ char_u *funcname; -+ pos_T pos; -+ win_T *curwin_save; -+ buf_T *curbuf_save; -+ typval_T rettv; -+ int save_State = State; -+ -+ funcname = (type == CTRL_X_FUNCTION) ? curbuf->b_p_cfu : curbuf->b_p_ofu; -+ if (*funcname == NUL) -+ return; -+ -+ // Call 'completefunc' to obtain the list of matches. -+ args[0].v_type = VAR_NUMBER; -+ args[0].vval.v_number = 0; -+ args[1].v_type = VAR_STRING; -+ args[1].vval.v_string = base != NULL ? base : (char_u *)""; -+ args[2].v_type = VAR_UNKNOWN; -+ -+ pos = curwin->w_cursor; -+ curwin_save = curwin; -+ curbuf_save = curbuf; -+ -+ // Call a function, which returns a list or dict. -+ if (call_vim_function(funcname, 2, args, &rettv) == OK) -+ { -+ switch (rettv.v_type) -+ { -+ case VAR_LIST: -+ matchlist = rettv.vval.v_list; -+ break; -+ case VAR_DICT: -+ matchdict = rettv.vval.v_dict; -+ break; -+ case VAR_SPECIAL: -+ if (rettv.vval.v_number == VVAL_NONE) -+ compl_opt_suppress_empty = TRUE; -+ // FALLTHROUGH -+ default: -+ // TODO: Give error message? -+ clear_tv(&rettv); -+ break; -+ } -+ } -+ -+ if (curwin_save != curwin || curbuf_save != curbuf) -+ { -+ emsg(_(e_complwin)); -+ goto theend; -+ } -+ curwin->w_cursor = pos; // restore the cursor position -+ validate_cursor(); -+ if (!EQUAL_POS(curwin->w_cursor, pos)) -+ { -+ emsg(_(e_compldel)); -+ goto theend; -+ } -+ -+ if (matchlist != NULL) -+ ins_compl_add_list(matchlist); -+ else if (matchdict != NULL) -+ ins_compl_add_dict(matchdict); -+ -+ theend: -+ // Restore State, it might have been changed. -+ State = save_State; -+ -+ if (matchdict != NULL) -+ dict_unref(matchdict); -+ if (matchlist != NULL) -+ list_unref(matchlist); -+ } -+ #endif // FEAT_COMPL_FUNC -+ -+ #if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) || defined(PROTO) -+ /* -+ * Add completions from a list. -+ */ -+ static void -+ ins_compl_add_list(list_T *list) -+ { -+ listitem_T *li; -+ int dir = compl_direction; -+ -+ // Go through the List with matches and add each of them. -+ for (li = list->lv_first; li != NULL; li = li->li_next) -+ { -+ if (ins_compl_add_tv(&li->li_tv, dir) == OK) -+ // if dir was BACKWARD then honor it just once -+ dir = FORWARD; -+ else if (did_emsg) -+ break; -+ } -+ } -+ -+ /* -+ * Add completions from a dict. -+ */ -+ static void -+ ins_compl_add_dict(dict_T *dict) -+ { -+ dictitem_T *di_refresh; -+ dictitem_T *di_words; -+ -+ // Check for optional "refresh" item. -+ compl_opt_refresh_always = FALSE; -+ di_refresh = dict_find(dict, (char_u *)"refresh", 7); -+ if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) -+ { -+ char_u *v = di_refresh->di_tv.vval.v_string; -+ -+ if (v != NULL && STRCMP(v, (char_u *)"always") == 0) -+ compl_opt_refresh_always = TRUE; -+ } -+ -+ // Add completions from a "words" list. -+ di_words = dict_find(dict, (char_u *)"words", 5); -+ if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) -+ ins_compl_add_list(di_words->di_tv.vval.v_list); -+ } -+ -+ /* -+ * Add a match to the list of matches from a typeval_T. -+ * 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. -+ */ -+ int -+ ins_compl_add_tv(typval_T *tv, int dir) -+ { -+ char_u *word; -+ int icase = FALSE; -+ int adup = FALSE; -+ int aempty = FALSE; -+ char_u *(cptext[CPT_COUNT]); -+ -+ if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) -+ { -+ word = dict_get_string(tv->vval.v_dict, (char_u *)"word", FALSE); -+ cptext[CPT_ABBR] = dict_get_string(tv->vval.v_dict, -+ (char_u *)"abbr", FALSE); -+ cptext[CPT_MENU] = dict_get_string(tv->vval.v_dict, -+ (char_u *)"menu", FALSE); -+ cptext[CPT_KIND] = dict_get_string(tv->vval.v_dict, -+ (char_u *)"kind", FALSE); -+ cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict, -+ (char_u *)"info", FALSE); -+ cptext[CPT_USER_DATA] = dict_get_string(tv->vval.v_dict, -+ (char_u *)"user_data", FALSE); -+ if (dict_get_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL) -+ icase = dict_get_number(tv->vval.v_dict, (char_u *)"icase"); -+ if (dict_get_string(tv->vval.v_dict, (char_u *)"dup", FALSE) != NULL) -+ adup = dict_get_number(tv->vval.v_dict, (char_u *)"dup"); -+ if (dict_get_string(tv->vval.v_dict, (char_u *)"empty", FALSE) != NULL) -+ aempty = dict_get_number(tv->vval.v_dict, (char_u *)"empty"); -+ } -+ else -+ { -+ word = tv_get_string_chk(tv); -+ vim_memset(cptext, 0, sizeof(cptext)); -+ } -+ if (word == NULL || (!aempty && *word == NUL)) -+ return FAIL; -+ return ins_compl_add(word, -1, icase, NULL, cptext, dir, 0, adup); -+ } -+ #endif -+ -+ /* -+ * Get the next expansion(s), using "compl_pattern". -+ * The search starts at position "ini" in curbuf and in the direction -+ * compl_direction. -+ * When "compl_started" is FALSE start at that position, otherwise continue -+ * where we stopped searching before. -+ * This may return before finding all the matches. -+ * Return the total number of matches or -1 if still unknown -- Acevedo -+ */ -+ static int -+ ins_compl_get_exp(pos_T *ini) -+ { -+ static pos_T first_match_pos; -+ static pos_T last_match_pos; -+ static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' -+ static int found_all = FALSE; // Found all matches of a -+ // certain type. -+ static buf_T *ins_buf = NULL; // buffer being scanned -+ -+ pos_T *pos; -+ char_u **matches; -+ int save_p_scs; -+ int save_p_ws; -+ int save_p_ic; -+ int i; -+ int num_matches; -+ int len; -+ int found_new_match; -+ int type = ctrl_x_mode; -+ char_u *ptr; -+ char_u *dict = NULL; -+ int dict_f = 0; -+ int set_match_pos; -+ -+ if (!compl_started) -+ { -+ FOR_ALL_BUFFERS(ins_buf) -+ ins_buf->b_scanned = 0; -+ found_all = FALSE; -+ ins_buf = curbuf; -+ e_cpt = (compl_cont_status & CONT_LOCAL) -+ ? (char_u *)"." : curbuf->b_p_cpt; -+ last_match_pos = first_match_pos = *ini; -+ } -+ else if (ins_buf != curbuf && !buf_valid(ins_buf)) -+ ins_buf = curbuf; // In case the buffer was wiped out. -+ -+ compl_old_match = compl_curr_match; // remember the last current match -+ pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; -+ -+ // For ^N/^P loop over all the flags/windows/buffers in 'complete'. -+ for (;;) -+ { -+ found_new_match = FAIL; -+ set_match_pos = FALSE; -+ -+ // For ^N/^P pick a new entry from e_cpt if compl_started is off, -+ // or if found_all says this entry is done. For ^X^L only use the -+ // entries from 'complete' that look in loaded buffers. -+ if ((ctrl_x_mode == CTRL_X_NORMAL -+ || ctrl_x_mode_line_or_eval()) -+ && (!compl_started || found_all)) -+ { -+ found_all = FALSE; -+ while (*e_cpt == ',' || *e_cpt == ' ') -+ e_cpt++; -+ if (*e_cpt == '.' && !curbuf->b_scanned) -+ { -+ ins_buf = curbuf; -+ first_match_pos = *ini; -+ // Move the cursor back one character so that ^N can match the -+ // word immediately after the cursor. -+ if (ctrl_x_mode == CTRL_X_NORMAL && dec(&first_match_pos) < 0) -+ { -+ // Move the cursor to after the last character in the -+ // buffer, so that word at start of buffer is found -+ // correctly. -+ first_match_pos.lnum = ins_buf->b_ml.ml_line_count; -+ first_match_pos.col = -+ (colnr_T)STRLEN(ml_get(first_match_pos.lnum)); -+ } -+ last_match_pos = first_match_pos; -+ type = 0; -+ -+ // Remember the first match so that the loop stops when we -+ // wrap and come back there a second time. -+ set_match_pos = TRUE; -+ } -+ else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL -+ && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) -+ { -+ // Scan a buffer, but not the current one. -+ if (ins_buf->b_ml.ml_mfp != NULL) // loaded buffer -+ { -+ compl_started = TRUE; -+ first_match_pos.col = last_match_pos.col = 0; -+ first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; -+ last_match_pos.lnum = 0; -+ type = 0; -+ } -+ else // unloaded buffer, scan like dictionary -+ { -+ found_all = TRUE; -+ if (ins_buf->b_fname == NULL) -+ continue; -+ type = CTRL_X_DICTIONARY; -+ dict = ins_buf->b_fname; -+ dict_f = DICT_EXACT; -+ } -+ vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), -+ ins_buf->b_fname == NULL -+ ? buf_spname(ins_buf) -+ : ins_buf->b_sfname == NULL -+ ? ins_buf->b_fname -+ : ins_buf->b_sfname); -+ (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); -+ } -+ else if (*e_cpt == NUL) -+ break; -+ else -+ { -+ if (ctrl_x_mode_line_or_eval()) -+ type = -1; -+ else if (*e_cpt == 'k' || *e_cpt == 's') -+ { -+ if (*e_cpt == 'k') -+ type = CTRL_X_DICTIONARY; -+ else -+ type = CTRL_X_THESAURUS; -+ if (*++e_cpt != ',' && *e_cpt != NUL) -+ { -+ dict = e_cpt; -+ dict_f = DICT_FIRST; -+ } -+ } -+ #ifdef FEAT_FIND_ID -+ else if (*e_cpt == 'i') -+ type = CTRL_X_PATH_PATTERNS; -+ else if (*e_cpt == 'd') -+ type = CTRL_X_PATH_DEFINES; -+ #endif -+ else if (*e_cpt == ']' || *e_cpt == 't') -+ { -+ type = CTRL_X_TAGS; -+ vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags.")); -+ (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); -+ } -+ else -+ type = -1; -+ -+ // in any case e_cpt is advanced to the next entry -+ (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ","); -+ -+ found_all = TRUE; -+ if (type == -1) -+ continue; -+ } -+ } -+ -+ // If complete() was called then compl_pattern has been reset. The -+ // following won't work then, bail out. -+ if (compl_pattern == NULL) -+ break; -+ -+ switch (type) -+ { -+ case -1: -+ break; -+ #ifdef FEAT_FIND_ID -+ case CTRL_X_PATH_PATTERNS: -+ case CTRL_X_PATH_DEFINES: -+ find_pattern_in_path(compl_pattern, compl_direction, -+ (int)STRLEN(compl_pattern), FALSE, FALSE, -+ (type == CTRL_X_PATH_DEFINES -+ && !(compl_cont_status & CONT_SOL)) -+ ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND, -+ (linenr_T)1, (linenr_T)MAXLNUM); -+ break; -+ #endif -+ -+ case CTRL_X_DICTIONARY: -+ case CTRL_X_THESAURUS: -+ ins_compl_dictionaries( -+ dict != NULL ? dict -+ : (type == CTRL_X_THESAURUS -+ ? (*curbuf->b_p_tsr == NUL -+ ? p_tsr -+ : curbuf->b_p_tsr) -+ : (*curbuf->b_p_dict == NUL -+ ? p_dict -+ : curbuf->b_p_dict)), -+ compl_pattern, -+ dict != NULL ? dict_f -+ : 0, type == CTRL_X_THESAURUS); -+ dict = NULL; -+ break; -+ -+ case CTRL_X_TAGS: -+ // set p_ic according to p_ic, p_scs and pat for find_tags(). -+ save_p_ic = p_ic; -+ p_ic = ignorecase(compl_pattern); -+ -+ // Find up to TAG_MANY matches. Avoids that an enormous number -+ // of matches is found when compl_pattern is empty -+ if (find_tags(compl_pattern, &num_matches, &matches, -+ TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP -+ | (ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0), -+ TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) -+ { -+ ins_compl_add_matches(num_matches, matches, p_ic); -+ } -+ p_ic = save_p_ic; -+ break; -+ -+ case CTRL_X_FILES: -+ if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, -+ EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) -+ { -+ -+ // May change home directory back to "~". -+ tilde_replace(compl_pattern, num_matches, matches); -+ ins_compl_add_matches(num_matches, matches, p_fic || p_wic); -+ } -+ break; -+ -+ case CTRL_X_CMDLINE: -+ if (expand_cmdline(&compl_xp, compl_pattern, -+ (int)STRLEN(compl_pattern), -+ &num_matches, &matches) == EXPAND_OK) -+ ins_compl_add_matches(num_matches, matches, FALSE); -+ break; -+ -+ #ifdef FEAT_COMPL_FUNC -+ case CTRL_X_FUNCTION: -+ case CTRL_X_OMNI: -+ expand_by_function(type, compl_pattern); -+ break; -+ #endif -+ -+ case CTRL_X_SPELL: -+ #ifdef FEAT_SPELL -+ num_matches = expand_spelling(first_match_pos.lnum, -+ compl_pattern, &matches); -+ if (num_matches > 0) -+ ins_compl_add_matches(num_matches, matches, p_ic); -+ #endif -+ break; -+ -+ default: // normal ^P/^N and ^X^L -+ // If 'infercase' is set, don't use 'smartcase' here -+ save_p_scs = p_scs; -+ if (ins_buf->b_p_inf) -+ p_scs = FALSE; -+ -+ // Buffers other than curbuf are scanned from the beginning or the -+ // end but never from the middle, thus setting nowrapscan in this -+ // buffers is a good idea, on the other hand, we always set -+ // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb -+ save_p_ws = p_ws; -+ if (ins_buf != curbuf) -+ p_ws = FALSE; -+ else if (*e_cpt == '.') -+ p_ws = TRUE; -+ for (;;) -+ { -+ int flags = 0; -+ -+ ++msg_silent; // Don't want messages for wrapscan. -+ -+ // ctrl_x_mode_line_or_eval() || word-wise search that -+ // has added a word that was at the beginning of the line -+ if (ctrl_x_mode_line_or_eval() -+ || (compl_cont_status & CONT_SOL)) -+ found_new_match = search_for_exact_line(ins_buf, pos, -+ compl_direction, compl_pattern); -+ else -+ found_new_match = searchit(NULL, ins_buf, pos, NULL, -+ compl_direction, -+ compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, -+ RE_LAST, (linenr_T)0, NULL, NULL); -+ --msg_silent; -+ if (!compl_started || set_match_pos) -+ { -+ // set "compl_started" even on fail -+ compl_started = TRUE; -+ first_match_pos = *pos; -+ last_match_pos = *pos; -+ set_match_pos = FALSE; -+ } -+ else if (first_match_pos.lnum == last_match_pos.lnum -+ && first_match_pos.col == last_match_pos.col) -+ found_new_match = FAIL; -+ if (found_new_match == FAIL) -+ { -+ if (ins_buf == curbuf) -+ found_all = TRUE; -+ break; -+ } -+ -+ // when ADDING, the text before the cursor matches, skip it -+ if ( (compl_cont_status & CONT_ADDING) && ins_buf == curbuf -+ && ini->lnum == pos->lnum -+ && ini->col == pos->col) -+ continue; -+ ptr = ml_get_buf(ins_buf, pos->lnum, FALSE) + pos->col; -+ if (ctrl_x_mode_line_or_eval()) -+ { -+ if (compl_cont_status & CONT_ADDING) -+ { -+ if (pos->lnum >= ins_buf->b_ml.ml_line_count) -+ continue; -+ ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); -+ if (!p_paste) -+ ptr = skipwhite(ptr); -+ } -+ len = (int)STRLEN(ptr); -+ } -+ else -+ { -+ char_u *tmp_ptr = ptr; -+ -+ if (compl_cont_status & CONT_ADDING) -+ { -+ tmp_ptr += compl_length; -+ // Skip if already inside a word. -+ if (vim_iswordp(tmp_ptr)) -+ continue; -+ // Find start of next word. -+ tmp_ptr = find_word_start(tmp_ptr); -+ } -+ // Find end of this word. -+ tmp_ptr = find_word_end(tmp_ptr); -+ len = (int)(tmp_ptr - ptr); -+ -+ if ((compl_cont_status & CONT_ADDING) -+ && len == compl_length) -+ { -+ if (pos->lnum < ins_buf->b_ml.ml_line_count) -+ { -+ // Try next line, if any. the new word will be -+ // "join" as if the normal command "J" was used. -+ // IOSIZE is always greater than -+ // compl_length, so the next STRNCPY always -+ // works -- Acevedo -+ STRNCPY(IObuff, ptr, len); -+ ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); -+ tmp_ptr = ptr = skipwhite(ptr); -+ // Find start of next word. -+ tmp_ptr = find_word_start(tmp_ptr); -+ // Find end of next word. -+ tmp_ptr = find_word_end(tmp_ptr); -+ if (tmp_ptr > ptr) -+ { -+ if (*ptr != ')' && IObuff[len - 1] != TAB) -+ { -+ if (IObuff[len - 1] != ' ') -+ IObuff[len++] = ' '; -+ // IObuf =~ "\k.* ", thus len >= 2 -+ if (p_js -+ && (IObuff[len - 2] == '.' -+ || (vim_strchr(p_cpo, CPO_JOINSP) -+ == NULL -+ && (IObuff[len - 2] == '?' -+ || IObuff[len - 2] == '!')))) -+ IObuff[len++] = ' '; -+ } -+ // copy as much as possible of the new word -+ if (tmp_ptr - ptr >= IOSIZE - len) -+ tmp_ptr = ptr + IOSIZE - len - 1; -+ STRNCPY(IObuff + len, ptr, tmp_ptr - ptr); -+ len += (int)(tmp_ptr - ptr); -+ flags |= CONT_S_IPOS; -+ } -+ IObuff[len] = NUL; -+ ptr = IObuff; -+ } -+ if (len == compl_length) -+ continue; -+ } -+ } -+ if (ins_compl_add_infercase(ptr, len, p_ic, -+ ins_buf == curbuf ? NULL : ins_buf->b_sfname, -+ 0, flags) != NOTDONE) -+ { -+ found_new_match = OK; -+ break; -+ } -+ } -+ p_scs = save_p_scs; -+ p_ws = save_p_ws; -+ } -+ -+ // check if compl_curr_match has changed, (e.g. other type of -+ // expansion added something) -+ if (type != 0 && compl_curr_match != compl_old_match) -+ found_new_match = OK; -+ -+ // break the loop for specialized modes (use 'complete' just for the -+ // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new -+ // match -+ if ((ctrl_x_mode != CTRL_X_NORMAL -+ && !ctrl_x_mode_line_or_eval()) || found_new_match != FAIL) -+ { -+ if (got_int) -+ break; -+ // Fill the popup menu as soon as possible. -+ if (type != -1) -+ ins_compl_check_keys(0, FALSE); -+ -+ if ((ctrl_x_mode != CTRL_X_NORMAL -+ && !ctrl_x_mode_line_or_eval()) || compl_interrupted) -+ break; -+ compl_started = TRUE; -+ } -+ else -+ { -+ // Mark a buffer scanned when it has been scanned completely -+ if (type == 0 || type == CTRL_X_PATH_PATTERNS) -+ ins_buf->b_scanned = TRUE; -+ -+ compl_started = FALSE; -+ } -+ } -+ compl_started = TRUE; -+ -+ if ((ctrl_x_mode == CTRL_X_NORMAL || ctrl_x_mode_line_or_eval()) -+ && *e_cpt == NUL) // Got to end of 'complete' -+ found_new_match = FAIL; -+ -+ i = -1; // total of matches, unknown -+ if (found_new_match == FAIL || (ctrl_x_mode != CTRL_X_NORMAL -+ && !ctrl_x_mode_line_or_eval())) -+ i = ins_compl_make_cyclic(); -+ -+ if (compl_old_match != NULL) -+ { -+ // If several matches were added (FORWARD) or the search failed and has -+ // just been made cyclic then we have to move compl_curr_match to the -+ // next or previous entry (if any) -- Acevedo -+ compl_curr_match = compl_direction == FORWARD ? compl_old_match->cp_next -+ : compl_old_match->cp_prev; -+ if (compl_curr_match == NULL) -+ compl_curr_match = compl_old_match; -+ } -+ return i; -+ } -+ -+ /* -+ * Delete the old text being completed. -+ */ -+ void -+ ins_compl_delete(void) -+ { -+ int col; -+ -+ // In insert mode: Delete the typed part. -+ // In replace mode: Put the old characters back, if any. -+ col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); -+ if ((int)curwin->w_cursor.col > col) -+ { -+ if (stop_arrow() == FAIL) -+ return; -+ backspace_until_column(col); -+ } -+ -+ // TODO: is this sufficient for redrawing? Redrawing everything causes -+ // flicker, thus we can't do that. -+ changed_cline_bef_curs(); -+ // clear v:completed_item -+ set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); -+ } -+ -+ /* -+ * Insert the new text being completed. -+ * "in_compl_func" is TRUE when called from complete_check(). -+ */ -+ void -+ ins_compl_insert(int in_compl_func) -+ { -+ dict_T *dict; -+ -+ ins_bytes(compl_shown_match->cp_str + ins_compl_len()); -+ if (compl_shown_match->cp_flags & ORIGINAL_TEXT) -+ compl_used_match = FALSE; -+ else -+ compl_used_match = TRUE; -+ -+ // Set completed item. -+ // { word, abbr, menu, kind, info } -+ dict = dict_alloc_lock(VAR_FIXED); -+ if (dict != NULL) -+ { -+ dict_add_string(dict, "word", compl_shown_match->cp_str); -+ dict_add_string(dict, "abbr", compl_shown_match->cp_text[CPT_ABBR]); -+ dict_add_string(dict, "menu", compl_shown_match->cp_text[CPT_MENU]); -+ dict_add_string(dict, "kind", compl_shown_match->cp_text[CPT_KIND]); -+ dict_add_string(dict, "info", compl_shown_match->cp_text[CPT_INFO]); -+ dict_add_string(dict, "user_data", -+ compl_shown_match->cp_text[CPT_USER_DATA]); -+ } -+ set_vim_var_dict(VV_COMPLETED_ITEM, dict); -+ if (!in_compl_func) -+ compl_curr_match = compl_shown_match; -+ } -+ -+ /* -+ * Fill in the next completion in the current direction. -+ * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to -+ * get more completions. If it is FALSE, then we just do nothing when there -+ * are no more completions in a given direction. The latter case is used when -+ * we are still in the middle of finding completions, to allow browsing -+ * through the ones found so far. -+ * Return the total number of matches, or -1 if still unknown -- webb. -+ * -+ * compl_curr_match is currently being used by ins_compl_get_exp(), so we use -+ * compl_shown_match here. -+ * -+ * Note that this function may be called recursively once only. First with -+ * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn -+ * calls this function with "allow_get_expansion" FALSE. -+ */ -+ static int -+ ins_compl_next( -+ int allow_get_expansion, -+ int count, // repeat completion this many times; should -+ // be at least 1 -+ int insert_match, // Insert the newly selected match -+ int in_compl_func) // called from complete_check() -+ { -+ int num_matches = -1; -+ int todo = count; -+ compl_T *found_compl = NULL; -+ int found_end = FALSE; -+ int advance; -+ int started = compl_started; -+ -+ // When user complete function return -1 for findstart which is next -+ // time of 'always', compl_shown_match become NULL. -+ if (compl_shown_match == NULL) -+ return -1; -+ -+ if (compl_leader != NULL -+ && (compl_shown_match->cp_flags & ORIGINAL_TEXT) == 0) -+ { -+ // Set "compl_shown_match" to the actually shown match, it may differ -+ // when "compl_leader" is used to omit some of the matches. -+ while (!ins_compl_equal(compl_shown_match, -+ compl_leader, (int)STRLEN(compl_leader)) -+ && compl_shown_match->cp_next != NULL -+ && compl_shown_match->cp_next != compl_first_match) -+ compl_shown_match = compl_shown_match->cp_next; -+ -+ // If we didn't find it searching forward, and compl_shows_dir is -+ // backward, find the last match. -+ if (compl_shows_dir == BACKWARD -+ && !ins_compl_equal(compl_shown_match, -+ compl_leader, (int)STRLEN(compl_leader)) -+ && (compl_shown_match->cp_next == NULL -+ || compl_shown_match->cp_next == compl_first_match)) -+ { -+ while (!ins_compl_equal(compl_shown_match, -+ compl_leader, (int)STRLEN(compl_leader)) -+ && compl_shown_match->cp_prev != NULL -+ && compl_shown_match->cp_prev != compl_first_match) -+ compl_shown_match = compl_shown_match->cp_prev; -+ } -+ } -+ -+ if (allow_get_expansion && insert_match -+ && (!(compl_get_longest || compl_restarting) || compl_used_match)) -+ // Delete old text to be replaced -+ ins_compl_delete(); -+ -+ // When finding the longest common text we stick at the original text, -+ // don't let CTRL-N or CTRL-P move to the first match. -+ advance = count != 1 || !allow_get_expansion || !compl_get_longest; -+ -+ // When restarting the search don't insert the first match either. -+ if (compl_restarting) -+ { -+ advance = FALSE; -+ compl_restarting = FALSE; -+ } -+ -+ // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap -+ // around. -+ while (--todo >= 0) -+ { -+ if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) -+ { -+ compl_shown_match = compl_shown_match->cp_next; -+ found_end = (compl_first_match != NULL -+ && (compl_shown_match->cp_next == compl_first_match -+ || compl_shown_match == compl_first_match)); -+ } -+ else if (compl_shows_dir == BACKWARD -+ && compl_shown_match->cp_prev != NULL) -+ { -+ found_end = (compl_shown_match == compl_first_match); -+ compl_shown_match = compl_shown_match->cp_prev; -+ found_end |= (compl_shown_match == compl_first_match); -+ } -+ else -+ { -+ if (!allow_get_expansion) -+ { -+ if (advance) -+ { -+ if (compl_shows_dir == BACKWARD) -+ compl_pending -= todo + 1; -+ else -+ compl_pending += todo + 1; -+ } -+ return -1; -+ } -+ -+ if (!compl_no_select && advance) -+ { -+ if (compl_shows_dir == BACKWARD) -+ --compl_pending; -+ else -+ ++compl_pending; -+ } -+ -+ // Find matches. -+ num_matches = ins_compl_get_exp(&compl_startpos); -+ -+ // handle any pending completions -+ while (compl_pending != 0 && compl_direction == compl_shows_dir -+ && advance) -+ { -+ if (compl_pending > 0 && compl_shown_match->cp_next != NULL) -+ { -+ compl_shown_match = compl_shown_match->cp_next; -+ --compl_pending; -+ } -+ if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) -+ { -+ compl_shown_match = compl_shown_match->cp_prev; -+ ++compl_pending; -+ } -+ else -+ break; -+ } -+ found_end = FALSE; -+ } -+ if ((compl_shown_match->cp_flags & ORIGINAL_TEXT) == 0 -+ && compl_leader != NULL -+ && !ins_compl_equal(compl_shown_match, -+ compl_leader, (int)STRLEN(compl_leader))) -+ ++todo; -+ else -+ // Remember a matching item. -+ found_compl = compl_shown_match; -+ -+ // Stop at the end of the list when we found a usable match. -+ if (found_end) -+ { -+ if (found_compl != NULL) -+ { -+ compl_shown_match = found_compl; -+ break; -+ } -+ todo = 1; // use first usable match after wrapping around -+ } -+ } -+ -+ // Insert the text of the new completion, or the compl_leader. -+ if (compl_no_insert && !started) -+ { -+ ins_bytes(compl_orig_text + ins_compl_len()); -+ compl_used_match = FALSE; -+ } -+ else if (insert_match) -+ { -+ if (!compl_get_longest || compl_used_match) -+ ins_compl_insert(in_compl_func); -+ else -+ ins_bytes(compl_leader + ins_compl_len()); -+ } -+ else -+ compl_used_match = FALSE; -+ -+ if (!allow_get_expansion) -+ { -+ // may undisplay the popup menu first -+ ins_compl_upd_pum(); -+ -+ if (pum_enough_matches()) -+ // Will display the popup menu, don't redraw yet to avoid flicker. -+ pum_call_update_screen(); -+ else -+ // Not showing the popup menu yet, redraw to show the user what was -+ // inserted. -+ update_screen(0); -+ -+ // display the updated popup menu -+ ins_compl_show_pum(); -+ #ifdef FEAT_GUI -+ if (gui.in_use) -+ { -+ // Show the cursor after the match, not after the redrawn text. -+ setcursor(); -+ out_flush_cursor(FALSE, FALSE); -+ } -+ #endif -+ -+ // Delete old text to be replaced, since we're still searching and -+ // don't want to match ourselves! -+ ins_compl_delete(); -+ } -+ -+ // Enter will select a match when the match wasn't inserted and the popup -+ // menu is visible. -+ if (compl_no_insert && !started) -+ compl_enter_selects = TRUE; -+ else -+ compl_enter_selects = !insert_match && compl_match_array != NULL; -+ -+ // Show the file name for the match (if any) -+ // Truncate the file name to avoid a wait for return. -+ if (compl_shown_match->cp_fname != NULL) -+ { -+ char *lead = _("match in file"); -+ int space = sc_col - vim_strsize((char_u *)lead) - 2; -+ char_u *s; -+ char_u *e; -+ -+ if (space > 0) -+ { -+ // We need the tail that fits. With double-byte encoding going -+ // back from the end is very slow, thus go from the start and keep -+ // the text that fits in "space" between "s" and "e". -+ for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) -+ { -+ space -= ptr2cells(e); -+ while (space < 0) -+ { -+ space += ptr2cells(s); -+ MB_PTR_ADV(s); -+ } -+ } -+ vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, -+ s > compl_shown_match->cp_fname ? "<" : "", s); -+ msg((char *)IObuff); -+ redraw_cmdline = FALSE; // don't overwrite! -+ } -+ } -+ -+ return num_matches; -+ } -+ -+ /* -+ * Call this while finding completions, to check whether the user has hit a key -+ * that should change the currently displayed completion, or exit completion -+ * mode. Also, when compl_pending is not zero, show a completion as soon as -+ * possible. -- webb -+ * "frequency" specifies out of how many calls we actually check. -+ * "in_compl_func" is TRUE when called from complete_check(), don't set -+ * compl_curr_match. -+ */ -+ void -+ ins_compl_check_keys(int frequency, int in_compl_func) -+ { -+ static int count = 0; -+ int c; -+ -+ // Don't check when reading keys from a script, :normal or feedkeys(). -+ // That would break the test scripts. But do check for keys when called -+ // from complete_check(). -+ if (!in_compl_func && (using_script() || ex_normal_busy)) -+ return; -+ -+ // Only do this at regular intervals -+ if (++count < frequency) -+ return; -+ count = 0; -+ -+ // Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key() -+ // can't do its work correctly. -+ c = vpeekc_any(); -+ if (c != NUL) -+ { -+ if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) -+ { -+ c = safe_vgetc(); // Eat the character -+ compl_shows_dir = ins_compl_key2dir(c); -+ (void)ins_compl_next(FALSE, ins_compl_key2count(c), -+ c != K_UP && c != K_DOWN, in_compl_func); -+ } -+ else -+ { -+ // Need to get the character to have KeyTyped set. We'll put it -+ // back with vungetc() below. But skip K_IGNORE. -+ c = safe_vgetc(); -+ if (c != K_IGNORE) -+ { -+ // Don't interrupt completion when the character wasn't typed, -+ // e.g., when doing @q to replay keys. -+ if (c != Ctrl_R && KeyTyped) -+ compl_interrupted = TRUE; -+ -+ vungetc(c); -+ } -+ } -+ } -+ if (compl_pending != 0 && !got_int && !compl_no_insert) -+ { -+ int todo = compl_pending > 0 ? compl_pending : -compl_pending; -+ -+ compl_pending = 0; -+ (void)ins_compl_next(FALSE, todo, TRUE, in_compl_func); -+ } -+ } -+ -+ /* -+ * Decide the direction of Insert mode complete from the key typed. -+ * Returns BACKWARD or FORWARD. -+ */ -+ static int -+ ins_compl_key2dir(int c) -+ { -+ if (c == Ctrl_P || c == Ctrl_L -+ || c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP || c == K_UP) -+ return BACKWARD; -+ return FORWARD; -+ } -+ -+ /* -+ * Return TRUE for keys that are used for completion only when the popup menu -+ * is visible. -+ */ -+ static int -+ ins_compl_pum_key(int c) -+ { -+ return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP -+ || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == K_S_DOWN -+ || c == K_UP || c == K_DOWN); -+ } -+ -+ /* -+ * Decide the number of completions to move forward. -+ * Returns 1 for most keys, height of the popup menu for page-up/down keys. -+ */ -+ static int -+ ins_compl_key2count(int c) -+ { -+ int h; -+ -+ if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) -+ { -+ h = pum_get_height(); -+ if (h > 3) -+ h -= 2; // keep some context -+ return h; -+ } -+ return 1; -+ } -+ -+ /* -+ * Return TRUE if completion with "c" should insert the match, FALSE if only -+ * to change the currently selected completion. -+ */ -+ static int -+ ins_compl_use_match(int c) -+ { -+ switch (c) -+ { -+ case K_UP: -+ case K_DOWN: -+ case K_PAGEDOWN: -+ case K_KPAGEDOWN: -+ case K_S_DOWN: -+ case K_PAGEUP: -+ case K_KPAGEUP: -+ case K_S_UP: -+ return FALSE; -+ } -+ return TRUE; -+ } -+ -+ /* -+ * Do Insert mode completion. -+ * Called when character "c" was typed, which has a meaning for completion. -+ * Returns OK if completion was done, FAIL if something failed (out of mem). -+ */ -+ int -+ ins_complete(int c, int enable_pum) -+ { -+ char_u *line; -+ int startcol = 0; // column where searched text starts -+ colnr_T curs_col; // cursor column -+ int n; -+ int save_w_wrow; -+ int save_w_leftcol; -+ int insert_match; -+ int save_did_ai = did_ai; -+ -+ compl_direction = ins_compl_key2dir(c); -+ insert_match = ins_compl_use_match(c); -+ -+ if (!compl_started) -+ { -+ // First time we hit ^N or ^P (in a row, I mean) -+ -+ did_ai = FALSE; -+ #ifdef FEAT_SMARTINDENT -+ did_si = FALSE; -+ can_si = FALSE; -+ can_si_back = FALSE; -+ #endif -+ if (stop_arrow() == FAIL) -+ return FAIL; -+ -+ line = ml_get(curwin->w_cursor.lnum); -+ curs_col = curwin->w_cursor.col; -+ compl_pending = 0; -+ -+ // If this same ctrl_x_mode has been interrupted use the text from -+ // "compl_startpos" to the cursor as a pattern to add a new word -+ // instead of expand the one before the cursor, in word-wise if -+ // "compl_startpos" is not in the same line as the cursor then fix it -+ // (the line has been split because it was longer than 'tw'). if SOL -+ // is set then skip the previous pattern, a word at the beginning of -+ // the line has been inserted, we'll look for that -- Acevedo. -+ if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT -+ && compl_cont_mode == ctrl_x_mode) -+ { -+ // it is a continued search -+ compl_cont_status &= ~CONT_INTRPT; // remove INTRPT -+ if (ctrl_x_mode == CTRL_X_NORMAL -+ || ctrl_x_mode == CTRL_X_PATH_PATTERNS -+ || ctrl_x_mode == CTRL_X_PATH_DEFINES) -+ { -+ if (compl_startpos.lnum != curwin->w_cursor.lnum) -+ { -+ // line (probably) wrapped, set compl_startpos to the -+ // first non_blank in the line, if it is not a wordchar -+ // include it to get a better pattern, but then we don't -+ // want the "\\<" prefix, check it bellow -+ compl_col = (colnr_T)getwhitecols(line); -+ compl_startpos.col = compl_col; -+ compl_startpos.lnum = curwin->w_cursor.lnum; -+ compl_cont_status &= ~CONT_SOL; // clear SOL if present -+ } -+ else -+ { -+ // S_IPOS was set when we inserted a word that was at the -+ // beginning of the line, which means that we'll go to SOL -+ // mode but first we need to redefine compl_startpos -+ if (compl_cont_status & CONT_S_IPOS) -+ { -+ compl_cont_status |= CONT_SOL; -+ compl_startpos.col = (colnr_T)(skipwhite( -+ line + compl_length -+ + compl_startpos.col) - line); -+ } -+ compl_col = compl_startpos.col; -+ } -+ compl_length = curwin->w_cursor.col - (int)compl_col; -+ // IObuff is used to add a "word from the next line" would we -+ // have enough space? just being paranoid -+ #define MIN_SPACE 75 -+ if (compl_length > (IOSIZE - MIN_SPACE)) -+ { -+ compl_cont_status &= ~CONT_SOL; -+ compl_length = (IOSIZE - MIN_SPACE); -+ compl_col = curwin->w_cursor.col - compl_length; -+ } -+ compl_cont_status |= CONT_ADDING | CONT_N_ADDS; -+ if (compl_length < 1) -+ compl_cont_status &= CONT_LOCAL; -+ } -+ else if (ctrl_x_mode_line_or_eval()) -+ compl_cont_status = CONT_ADDING | CONT_N_ADDS; -+ else -+ compl_cont_status = 0; -+ } -+ else -+ compl_cont_status &= CONT_LOCAL; -+ -+ if (!(compl_cont_status & CONT_ADDING)) // normal expansion -+ { -+ compl_cont_mode = ctrl_x_mode; -+ if (ctrl_x_mode != CTRL_X_NORMAL) -+ // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL -+ compl_cont_status = 0; -+ compl_cont_status |= CONT_N_ADDS; -+ compl_startpos = curwin->w_cursor; -+ startcol = (int)curs_col; -+ compl_col = 0; -+ } -+ -+ // Work out completion pattern and original text -- webb -+ if (ctrl_x_mode == CTRL_X_NORMAL || (ctrl_x_mode & CTRL_X_WANT_IDENT)) -+ { -+ if ((compl_cont_status & CONT_SOL) -+ || ctrl_x_mode == CTRL_X_PATH_DEFINES) -+ { -+ if (!(compl_cont_status & CONT_ADDING)) -+ { -+ while (--startcol >= 0 && vim_isIDc(line[startcol])) -+ ; -+ compl_col += ++startcol; -+ compl_length = curs_col - startcol; -+ } -+ if (p_ic) -+ compl_pattern = str_foldcase(line + compl_col, -+ compl_length, NULL, 0); -+ else -+ compl_pattern = vim_strnsave(line + compl_col, -+ compl_length); -+ if (compl_pattern == NULL) -+ return FAIL; -+ } -+ else if (compl_cont_status & CONT_ADDING) -+ { -+ char_u *prefix = (char_u *)"\\<"; -+ -+ // we need up to 2 extra chars for the prefix -+ compl_pattern = alloc(quote_meta(NULL, line + compl_col, -+ compl_length) + 2); -+ if (compl_pattern == NULL) -+ return FAIL; -+ if (!vim_iswordp(line + compl_col) -+ || (compl_col > 0 -+ && (vim_iswordp(mb_prevptr(line, line + compl_col))))) -+ prefix = (char_u *)""; -+ STRCPY((char *)compl_pattern, prefix); -+ (void)quote_meta(compl_pattern + STRLEN(prefix), -+ line + compl_col, compl_length); -+ } -+ else if (--startcol < 0 -+ || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) -+ { -+ // Match any word of at least two chars -+ compl_pattern = vim_strsave((char_u *)"\\<\\k\\k"); -+ if (compl_pattern == NULL) -+ return FAIL; -+ compl_col += curs_col; -+ compl_length = 0; -+ } -+ else -+ { -+ // Search the point of change class of multibyte character -+ // or not a word single byte character backward. -+ if (has_mbyte) -+ { -+ int base_class; -+ int head_off; -+ -+ startcol -= (*mb_head_off)(line, line + startcol); -+ base_class = mb_get_class(line + startcol); -+ while (--startcol >= 0) -+ { -+ head_off = (*mb_head_off)(line, line + startcol); -+ if (base_class != mb_get_class(line + startcol -+ - head_off)) -+ break; -+ startcol -= head_off; -+ } -+ } -+ else -+ while (--startcol >= 0 && vim_iswordc(line[startcol])) -+ ; -+ compl_col += ++startcol; -+ compl_length = (int)curs_col - startcol; -+ if (compl_length == 1) -+ { -+ // Only match word with at least two chars -- webb -+ // there's no need to call quote_meta, -+ // alloc(7) is enough -- Acevedo -+ compl_pattern = alloc(7); -+ if (compl_pattern == NULL) -+ return FAIL; -+ STRCPY((char *)compl_pattern, "\\<"); -+ (void)quote_meta(compl_pattern + 2, line + compl_col, 1); -+ STRCAT((char *)compl_pattern, "\\k"); -+ } -+ else -+ { -+ compl_pattern = alloc(quote_meta(NULL, line + compl_col, -+ compl_length) + 2); -+ if (compl_pattern == NULL) -+ return FAIL; -+ STRCPY((char *)compl_pattern, "\\<"); -+ (void)quote_meta(compl_pattern + 2, line + compl_col, -+ compl_length); -+ } -+ } -+ } -+ else if (ctrl_x_mode_line_or_eval()) -+ { -+ compl_col = (colnr_T)getwhitecols(line); -+ compl_length = (int)curs_col - (int)compl_col; -+ if (compl_length < 0) // cursor in indent: empty pattern -+ compl_length = 0; -+ if (p_ic) -+ compl_pattern = str_foldcase(line + compl_col, compl_length, -+ NULL, 0); -+ else -+ compl_pattern = vim_strnsave(line + compl_col, compl_length); -+ if (compl_pattern == NULL) -+ return FAIL; -+ } -+ else if (ctrl_x_mode == CTRL_X_FILES) -+ { -+ // Go back to just before the first filename character. -+ if (startcol > 0) -+ { -+ char_u *p = line + startcol; -+ -+ MB_PTR_BACK(line, p); -+ while (p > line && vim_isfilec(PTR2CHAR(p))) -+ MB_PTR_BACK(line, p); -+ if (p == line && vim_isfilec(PTR2CHAR(p))) -+ startcol = 0; -+ else -+ startcol = (int)(p - line) + 1; -+ } -+ -+ compl_col += startcol; -+ compl_length = (int)curs_col - startcol; -+ compl_pattern = addstar(line + compl_col, compl_length, -+ EXPAND_FILES); -+ if (compl_pattern == NULL) -+ return FAIL; -+ } -+ else if (ctrl_x_mode == CTRL_X_CMDLINE) -+ { -+ compl_pattern = vim_strnsave(line, curs_col); -+ if (compl_pattern == NULL) -+ return FAIL; -+ set_cmd_context(&compl_xp, compl_pattern, -+ (int)STRLEN(compl_pattern), curs_col, FALSE); -+ if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL -+ || compl_xp.xp_context == EXPAND_NOTHING) -+ // No completion possible, use an empty pattern to get a -+ // "pattern not found" message. -+ compl_col = curs_col; -+ else -+ compl_col = (int)(compl_xp.xp_pattern - compl_pattern); -+ compl_length = curs_col - compl_col; -+ } -+ else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) -+ { -+ #ifdef FEAT_COMPL_FUNC -+ // Call user defined function 'completefunc' with "a:findstart" -+ // set to 1 to obtain the length of text to use for completion. -+ typval_T args[3]; -+ int col; -+ char_u *funcname; -+ pos_T pos; -+ win_T *curwin_save; -+ buf_T *curbuf_save; -+ int save_State = State; -+ -+ // Call 'completefunc' or 'omnifunc' and get pattern length as a -+ // string -+ funcname = ctrl_x_mode == CTRL_X_FUNCTION -+ ? curbuf->b_p_cfu : curbuf->b_p_ofu; -+ if (*funcname == NUL) -+ { -+ semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION -+ ? "completefunc" : "omnifunc"); -+ // restore did_ai, so that adding comment leader works -+ did_ai = save_did_ai; -+ return FAIL; -+ } -+ -+ args[0].v_type = VAR_NUMBER; -+ args[0].vval.v_number = 1; -+ args[1].v_type = VAR_STRING; -+ args[1].vval.v_string = (char_u *)""; -+ args[2].v_type = VAR_UNKNOWN; -+ pos = curwin->w_cursor; -+ curwin_save = curwin; -+ curbuf_save = curbuf; -+ col = call_func_retnr(funcname, 2, args); -+ -+ State = save_State; -+ if (curwin_save != curwin || curbuf_save != curbuf) -+ { -+ emsg(_(e_complwin)); -+ return FAIL; -+ } -+ curwin->w_cursor = pos; // restore the cursor position -+ validate_cursor(); -+ if (!EQUAL_POS(curwin->w_cursor, pos)) -+ { -+ emsg(_(e_compldel)); -+ return FAIL; -+ } -+ -+ // Return value -2 means the user complete function wants to -+ // cancel the complete without an error. -+ // Return value -3 does the same as -2 and leaves CTRL-X mode. -+ if (col == -2) -+ return FAIL; -+ if (col == -3) -+ { -+ ctrl_x_mode = CTRL_X_NORMAL; -+ edit_submode = NULL; -+ if (!shortmess(SHM_COMPLETIONMENU)) -+ msg_clr_cmdline(); -+ return FAIL; -+ } -+ -+ // Reset extended parameters of completion, when start new -+ // completion. -+ compl_opt_refresh_always = FALSE; -+ compl_opt_suppress_empty = FALSE; -+ -+ if (col < 0) -+ col = curs_col; -+ compl_col = col; -+ if (compl_col > curs_col) -+ compl_col = curs_col; -+ -+ // Setup variables for completion. Need to obtain "line" again, -+ // it may have become invalid. -+ line = ml_get(curwin->w_cursor.lnum); -+ compl_length = curs_col - compl_col; -+ compl_pattern = vim_strnsave(line + compl_col, compl_length); -+ if (compl_pattern == NULL) -+ #endif -+ return FAIL; -+ } -+ else if (ctrl_x_mode == CTRL_X_SPELL) -+ { -+ #ifdef FEAT_SPELL -+ if (spell_bad_len > 0) -+ compl_col = curs_col - spell_bad_len; -+ else -+ compl_col = spell_word_start(startcol); -+ if (compl_col >= (colnr_T)startcol) -+ { -+ compl_length = 0; -+ compl_col = curs_col; -+ } -+ else -+ { -+ spell_expand_check_cap(compl_col); -+ compl_length = (int)curs_col - compl_col; -+ } -+ // Need to obtain "line" again, it may have become invalid. -+ line = ml_get(curwin->w_cursor.lnum); -+ compl_pattern = vim_strnsave(line + compl_col, compl_length); -+ if (compl_pattern == NULL) -+ #endif -+ return FAIL; -+ } -+ else -+ { -+ internal_error("ins_complete()"); -+ return FAIL; -+ } -+ -+ if (compl_cont_status & CONT_ADDING) -+ { -+ edit_submode_pre = (char_u *)_(" Adding"); -+ if (ctrl_x_mode_line_or_eval()) -+ { -+ // Insert a new line, keep indentation but ignore 'comments' -+ #ifdef FEAT_COMMENTS -+ char_u *old = curbuf->b_p_com; -+ -+ curbuf->b_p_com = (char_u *)""; -+ #endif -+ compl_startpos.lnum = curwin->w_cursor.lnum; -+ compl_startpos.col = compl_col; -+ ins_eol('\r'); -+ #ifdef FEAT_COMMENTS -+ curbuf->b_p_com = old; -+ #endif -+ compl_length = 0; -+ compl_col = curwin->w_cursor.col; -+ } -+ } -+ else -+ { -+ edit_submode_pre = NULL; -+ compl_startpos.col = compl_col; -+ } -+ -+ if (compl_cont_status & CONT_LOCAL) -+ edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); -+ else -+ edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); -+ -+ // If any of the original typed text has been changed we need to fix -+ // the redo buffer. -+ ins_compl_fixRedoBufForLeader(NULL); -+ -+ // Always add completion for the original text. -+ vim_free(compl_orig_text); -+ compl_orig_text = vim_strnsave(line + 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) -+ { -+ VIM_CLEAR(compl_pattern); -+ VIM_CLEAR(compl_orig_text); -+ return FAIL; -+ } -+ -+ // showmode might reset the internal line pointers, so it must -+ // be called before line = ml_get(), or when this address is no -+ // longer needed. -- Acevedo. -+ edit_submode_extra = (char_u *)_("-- Searching..."); -+ edit_submode_highl = HLF_COUNT; -+ showmode(); -+ edit_submode_extra = NULL; -+ out_flush(); -+ } -+ else if (insert_match && stop_arrow() == FAIL) -+ return FAIL; -+ -+ compl_shown_match = compl_curr_match; -+ compl_shows_dir = compl_direction; -+ -+ // Find next match (and following matches). -+ save_w_wrow = curwin->w_wrow; -+ save_w_leftcol = curwin->w_leftcol; -+ n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE); -+ -+ // may undisplay the popup menu -+ ins_compl_upd_pum(); -+ -+ if (n > 1) // all matches have been found -+ compl_matches = n; -+ compl_curr_match = compl_shown_match; -+ compl_direction = compl_shows_dir; -+ -+ // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert -+ // mode. -+ if (got_int && !global_busy) -+ { -+ (void)vgetc(); -+ got_int = FALSE; -+ } -+ -+ // we found no match if the list has only the "compl_orig_text"-entry -+ if (compl_first_match == compl_first_match->cp_next) -+ { -+ edit_submode_extra = (compl_cont_status & CONT_ADDING) -+ && compl_length > 1 -+ ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); -+ edit_submode_highl = HLF_E; -+ // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, -+ // because we couldn't expand anything at first place, but if we used -+ // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word -+ // (such as M in M'exico) if not tried already. -- Acevedo -+ if ( compl_length > 1 -+ || (compl_cont_status & CONT_ADDING) -+ || (ctrl_x_mode != CTRL_X_NORMAL -+ && ctrl_x_mode != CTRL_X_PATH_PATTERNS -+ && ctrl_x_mode != CTRL_X_PATH_DEFINES)) -+ compl_cont_status &= ~CONT_N_ADDS; -+ } -+ -+ if (compl_curr_match->cp_flags & CONT_S_IPOS) -+ compl_cont_status |= CONT_S_IPOS; -+ else -+ compl_cont_status &= ~CONT_S_IPOS; -+ -+ if (edit_submode_extra == NULL) -+ { -+ if (compl_curr_match->cp_flags & ORIGINAL_TEXT) -+ { -+ edit_submode_extra = (char_u *)_("Back at original"); -+ edit_submode_highl = HLF_W; -+ } -+ else if (compl_cont_status & CONT_S_IPOS) -+ { -+ edit_submode_extra = (char_u *)_("Word from other line"); -+ edit_submode_highl = HLF_COUNT; -+ } -+ else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) -+ { -+ edit_submode_extra = (char_u *)_("The only match"); -+ edit_submode_highl = HLF_COUNT; -+ } -+ else -+ { -+ // Update completion sequence number when needed. -+ if (compl_curr_match->cp_number == -1) -+ { -+ int number = 0; -+ compl_T *match; -+ -+ if (compl_direction == FORWARD) -+ { -+ // search backwards for the first valid (!= -1) number. -+ // This should normally succeed already at the first loop -+ // cycle, so it's fast! -+ for (match = compl_curr_match->cp_prev; match != NULL -+ && match != compl_first_match; -+ match = match->cp_prev) -+ if (match->cp_number != -1) -+ { -+ number = match->cp_number; -+ break; -+ } -+ if (match != NULL) -+ // go up and assign all numbers which are not assigned -+ // yet -+ for (match = match->cp_next; -+ match != NULL && match->cp_number == -1; -+ match = match->cp_next) -+ match->cp_number = ++number; -+ } -+ else // BACKWARD -+ { -+ // search forwards (upwards) for the first valid (!= -1) -+ // number. This should normally succeed already at the -+ // first loop cycle, so it's fast! -+ for (match = compl_curr_match->cp_next; match != NULL -+ && match != compl_first_match; -+ match = match->cp_next) -+ if (match->cp_number != -1) -+ { -+ number = match->cp_number; -+ break; -+ } -+ if (match != NULL) -+ // go down and assign all numbers which are not -+ // assigned yet -+ for (match = match->cp_prev; match -+ && match->cp_number == -1; -+ match = match->cp_prev) -+ match->cp_number = ++number; -+ } -+ } -+ -+ // The match should always have a sequence number now, this is -+ // just a safety check. -+ if (compl_curr_match->cp_number != -1) -+ { -+ // Space for 10 text chars. + 2x10-digit no.s = 31. -+ // Translations may need more than twice that. -+ static char_u match_ref[81]; -+ -+ if (compl_matches > 0) -+ vim_snprintf((char *)match_ref, sizeof(match_ref), -+ _("match %d of %d"), -+ compl_curr_match->cp_number, compl_matches); -+ else -+ vim_snprintf((char *)match_ref, sizeof(match_ref), -+ _("match %d"), -+ compl_curr_match->cp_number); -+ edit_submode_extra = match_ref; -+ edit_submode_highl = HLF_R; -+ if (dollar_vcol >= 0) -+ curs_columns(FALSE); -+ } -+ } -+ } -+ -+ // Show a message about what (completion) mode we're in. -+ if (!compl_opt_suppress_empty) -+ { -+ showmode(); -+ if (!shortmess(SHM_COMPLETIONMENU)) -+ { -+ if (edit_submode_extra != NULL) -+ { -+ if (!p_smd) -+ msg_attr((char *)edit_submode_extra, -+ edit_submode_highl < HLF_COUNT -+ ? HL_ATTR(edit_submode_highl) : 0); -+ } -+ else -+ msg_clr_cmdline(); // necessary for "noshowmode" -+ } -+ } -+ -+ // Show the popup menu, unless we got interrupted. -+ if (enable_pum && !compl_interrupted) -+ show_pum(save_w_wrow, save_w_leftcol); -+ -+ compl_was_interrupted = compl_interrupted; -+ compl_interrupted = FALSE; -+ -+ return OK; -+ } -+ -+ static void -+ show_pum(int prev_w_wrow, int prev_w_leftcol) -+ { -+ // RedrawingDisabled may be set when invoked through complete(). -+ int n = RedrawingDisabled; -+ -+ RedrawingDisabled = 0; -+ -+ // If the cursor moved or the display scrolled we need to remove the pum -+ // first. -+ setcursor(); -+ if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol) -+ ins_compl_del_pum(); -+ -+ ins_compl_show_pum(); -+ setcursor(); -+ RedrawingDisabled = n; -+ } -+ -+ /* -+ * Looks in the first "len" chars. of "src" for search-metachars. -+ * If dest is not NULL the chars. are copied there quoting (with -+ * a backslash) the metachars, and dest would be NUL terminated. -+ * Returns the length (needed) of dest -+ */ -+ static unsigned -+ quote_meta(char_u *dest, char_u *src, int len) -+ { -+ unsigned m = (unsigned)len + 1; // one extra for the NUL -+ -+ for ( ; --len >= 0; src++) -+ { -+ switch (*src) -+ { -+ case '.': -+ case '*': -+ case '[': -+ if (ctrl_x_mode == CTRL_X_DICTIONARY -+ || ctrl_x_mode == CTRL_X_THESAURUS) -+ break; -+ // FALLTHROUGH -+ case '~': -+ if (!p_magic) // quote these only if magic is set -+ break; -+ // FALLTHROUGH -+ case '\\': -+ if (ctrl_x_mode == CTRL_X_DICTIONARY -+ || ctrl_x_mode == CTRL_X_THESAURUS) -+ break; -+ // FALLTHROUGH -+ case '^': // currently it's not needed. -+ case '$': -+ m++; -+ if (dest != NULL) -+ *dest++ = '\\'; -+ break; -+ } -+ if (dest != NULL) -+ *dest++ = *src; -+ // Copy remaining bytes of a multibyte character. -+ if (has_mbyte) -+ { -+ int i, mb_len; -+ -+ mb_len = (*mb_ptr2len)(src) - 1; -+ if (mb_len > 0 && len >= mb_len) -+ for (i = 0; i < mb_len; ++i) -+ { -+ --len; -+ ++src; -+ if (dest != NULL) -+ *dest++ = *src; -+ } -+ } -+ } -+ if (dest != NULL) -+ *dest = NUL; -+ -+ return m; -+ } -+ -+ # if defined(EXITFREE) || defined(PROTO) -+ void -+ free_insexpand_stuff(void) -+ { -+ VIM_CLEAR(compl_orig_text); -+ } -+ # endif -+ -+ # ifdef FEAT_SPELL -+ /* -+ * Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly -+ * spelled word, if there is one. -+ */ -+ static void -+ spell_back_to_badword(void) -+ { -+ pos_T tpos = curwin->w_cursor; -+ -+ spell_bad_len = spell_move_to(curwin, BACKWARD, TRUE, TRUE, NULL); -+ if (curwin->w_cursor.col != tpos.col) -+ start_arrow(&tpos); -+ } -+ # endif -+ -+ #endif // FEAT_INS_EXPAND -*** ../vim-8.1.1075/src/misc2.c 2019-03-22 12:03:26.583533911 +0100 ---- src/misc2.c 2019-03-30 12:54:54.305407871 +0100 -*************** -*** 1130,1135 **** ---- 1130,1136 ---- - free_search_patterns(); - free_old_sub(); - free_last_insert(); -+ free_insexpand_stuff(); - free_prev_shellcmd(); - free_regexp_stuff(); - free_tag_stuff(); -*** ../vim-8.1.1075/src/proto.h 2019-02-19 21:34:01.987747438 +0100 ---- src/proto.h 2019-03-30 12:54:54.305407871 +0100 -*************** -*** 89,94 **** ---- 89,97 ---- - # include "hardcopy.pro" - # include "hashtab.pro" - # include "indent.pro" -+ # ifdef FEAT_INS_EXPAND -+ # include "insexpand.pro" -+ # endif - # include "json.pro" - # include "list.pro" - # include "blob.pro" -*** ../vim-8.1.1075/src/proto/edit.pro 2019-03-29 12:19:34.953348924 +0100 ---- src/proto/edit.pro 2019-03-30 12:56:24.004687230 +0100 -*************** -*** 1,5 **** ---- 1,7 ---- - /* edit.c */ - int edit(int cmdchar, int startln, long count); -+ int ins_need_undo_get(void); -+ void ins_redraw(int ready); - void edit_putchar(int c, int highlight); - char_u *prompt_text(void); - int prompt_curpos_editable(void); -*************** -*** 8,30 **** - void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes); - void truncate_spaces(char_u *line); - void backspace_until_column(int col); -- int ctrl_x_mode_not_default(void); -- int ctrl_x_mode_not_defined_yet(void); -- int vim_is_ctrl_x_key(int c); -- int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags); -- void completeopt_was_set(void); -- void set_completion(colnr_T startcol, list_T *list); -- void ins_compl_show_pum(void); -- char_u *find_word_start(char_u *ptr); -- char_u *find_word_end(char_u *ptr); -- int ins_compl_active(void); -- void get_complete_info(list_T *what_list, dict_T *retdict); -- int ins_compl_add_tv(typval_T *tv, int dir); -- void ins_compl_check_keys(int frequency, int in_compl_func); - int get_literal(void); - void insertchar(int c, int flags, int second_indent); - void auto_format(int trailblank, int prev_line); - int comp_textwidth(int ff); - int stop_arrow(void); - void set_last_insert(int c); - void free_last_insert(void); ---- 10,20 ---- - void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes); - void truncate_spaces(char_u *line); - void backspace_until_column(int col); - int get_literal(void); - void insertchar(int c, int flags, int second_indent); - void auto_format(int trailblank, int prev_line); - int comp_textwidth(int ff); -+ void start_arrow(pos_T *end_insert_pos); - int stop_arrow(void); - void set_last_insert(int c); - void free_last_insert(void); -*************** -*** 43,48 **** ---- 33,41 ---- - int bracketed_paste(paste_mode_T mode, int drop, garray_T *gap); - void ins_scroll(void); - void ins_horscroll(void); -+ int ins_eol(int c); - int ins_copychar(linenr_T lnum); - colnr_T get_nolist_virtcol(void); -+ int can_cindent_get(void); -+ int ins_apply_autocmds(event_T event); - /* vim: set ft=c : */ -*** ../vim-8.1.1075/src/proto/insexpand.pro 2019-03-30 13:32:12.906308579 +0100 ---- src/proto/insexpand.pro 2019-03-30 13:49:17.191991200 +0100 -*************** -*** 0 **** ---- 1,50 ---- -+ /* insexpand.c */ -+ void ins_ctrl_x(void); -+ int ctrl_x_mode_none(void); -+ int ctrl_x_mode_normal(void); -+ int ctrl_x_mode_scroll(void); -+ int ctrl_x_mode_whole_line(void); -+ int ctrl_x_mode_files(void); -+ int ctrl_x_mode_tags(void); -+ int ctrl_x_mode_path_patterns(void); -+ int ctrl_x_mode_path_defines(void); -+ int ctrl_x_mode_dictionary(void); -+ int ctrl_x_mode_thesaurus(void); -+ int ctrl_x_mode_cmdline(void); -+ int ctrl_x_mode_function(void); -+ int ctrl_x_mode_omni(void); -+ int ctrl_x_mode_spell(void); -+ int ctrl_x_mode_line_or_eval(void); -+ int ctrl_x_mode_not_default(void); -+ int ctrl_x_mode_not_defined_yet(void); -+ int has_compl_option(int dict_opt); -+ int vim_is_ctrl_x_key(int c); -+ int ins_compl_accept_char(int c); -+ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags); -+ int ins_compl_has_shown_match(void); -+ int ins_compl_long_shown_match(void); -+ void completeopt_was_set(void); -+ void set_completion(colnr_T startcol, list_T *list); -+ int pum_wanted(void); -+ void ins_compl_show_pum(void); -+ char_u *find_word_start(char_u *ptr); -+ char_u *find_word_end(char_u *ptr); -+ void ins_compl_clear(void); -+ int ins_compl_active(void); -+ void get_complete_info(list_T *what_list, dict_T *retdict); -+ int ins_compl_used_match(void); -+ void ins_compl_init_get_longest(void); -+ int ins_compl_interrupted(void); -+ int ins_compl_enter_selects(void); -+ colnr_T ins_compl_col(void); -+ int ins_compl_bs(void); -+ void ins_compl_addleader(int c); -+ void ins_compl_addfrommatch(void); -+ int ins_compl_prep(int c); -+ int ins_compl_add_tv(typval_T *tv, int dir); -+ void ins_compl_delete(void); -+ void ins_compl_insert(int in_compl_func); -+ void ins_compl_check_keys(int frequency, int in_compl_func); -+ int ins_complete(int c, int enable_pum); -+ void free_insexpand_stuff(void); -+ /* vim: set ft=c : */ -*** ../vim-8.1.1075/src/search.c 2019-03-03 14:42:04.782109771 +0100 ---- src/search.c 2019-03-30 12:54:54.305407871 +0100 -*************** -*** 5479,5485 **** - #ifdef FEAT_INS_EXPAND - if (action == ACTION_EXPAND) - ins_compl_check_keys(30, FALSE); -! if (got_int || compl_interrupted) - #else - if (got_int) - #endif ---- 5479,5485 ---- - #ifdef FEAT_INS_EXPAND - if (action == ACTION_EXPAND) - ins_compl_check_keys(30, FALSE); -! if (got_int || ins_compl_interrupted()) - #else - if (got_int) - #endif -*************** -*** 5550,5556 **** - ) - { - #ifdef FEAT_INS_EXPAND -! if (got_int || compl_interrupted) - #else - if (got_int) - #endif ---- 5550,5556 ---- - ) - { - #ifdef FEAT_INS_EXPAND -! if (got_int || ins_compl_interrupted()) - #else - if (got_int) - #endif -*** ../vim-8.1.1075/src/spell.c 2019-01-26 17:28:22.232599086 +0100 ---- src/spell.c 2019-03-30 13:33:40.365785214 +0100 -*************** -*** 8473,8479 **** - arridx[0] = 0; - curi[0] = 1; - while (depth >= 0 && !got_int -! && (pat == NULL || !compl_interrupted)) - { - if (curi[depth] > byts[arridx[depth]]) - { ---- 8473,8479 ---- - arridx[0] = 0; - curi[0] = 1; - while (depth >= 0 && !got_int -! && (pat == NULL || !ins_compl_interrupted())) - { - if (curi[depth] > byts[arridx[depth]]) - { -*** ../vim-8.1.1075/src/structs.h 2019-03-04 11:40:06.274241644 +0100 ---- src/structs.h 2019-03-30 13:47:56.176499590 +0100 -*************** -*** 1191,1226 **** - # ifdef PROTO - typedef long varnumber_T; - typedef unsigned long uvarnumber_T; -! #define VARNUM_MIN LONG_MIN -! #define VARNUM_MAX LONG_MAX -! #define UVARNUM_MAX ULONG_MAX - # else - typedef __int64 varnumber_T; - typedef unsigned __int64 uvarnumber_T; -! #define VARNUM_MIN _I64_MIN -! #define VARNUM_MAX _I64_MAX -! #define UVARNUM_MAX _UI64_MAX - # endif - # elif defined(HAVE_STDINT_H) - typedef int64_t varnumber_T; - typedef uint64_t uvarnumber_T; -! #define VARNUM_MIN INT64_MIN -! #define VARNUM_MAX INT64_MAX -! #define UVARNUM_MAX UINT64_MAX - # else - typedef long varnumber_T; - typedef unsigned long uvarnumber_T; -! #define VARNUM_MIN LONG_MIN -! #define VARNUM_MAX LONG_MAX -! #define UVARNUM_MAX ULONG_MAX - # endif - #else - /* Use 32-bit Number. */ - typedef int varnumber_T; - typedef unsigned int uvarnumber_T; -! #define VARNUM_MIN INT_MIN -! #define VARNUM_MAX INT_MAX -! #define UVARNUM_MAX UINT_MAX - #endif - - typedef double float_T; ---- 1191,1226 ---- - # ifdef PROTO - typedef long varnumber_T; - typedef unsigned long uvarnumber_T; -! # define VARNUM_MIN LONG_MIN -! # define VARNUM_MAX LONG_MAX -! # define UVARNUM_MAX ULONG_MAX - # else - typedef __int64 varnumber_T; - typedef unsigned __int64 uvarnumber_T; -! # define VARNUM_MIN _I64_MIN -! # define VARNUM_MAX _I64_MAX -! # define UVARNUM_MAX _UI64_MAX - # endif - # elif defined(HAVE_STDINT_H) - typedef int64_t varnumber_T; - typedef uint64_t uvarnumber_T; -! # define VARNUM_MIN INT64_MIN -! # define VARNUM_MAX INT64_MAX -! # define UVARNUM_MAX UINT64_MAX - # else - typedef long varnumber_T; - typedef unsigned long uvarnumber_T; -! # define VARNUM_MIN LONG_MIN -! # define VARNUM_MAX LONG_MAX -! # define UVARNUM_MAX ULONG_MAX - # endif - #else - /* Use 32-bit Number. */ - typedef int varnumber_T; - typedef unsigned int uvarnumber_T; -! # define VARNUM_MIN INT_MIN -! # define VARNUM_MAX INT_MAX -! # define UVARNUM_MAX UINT_MAX - #endif - - typedef double float_T; -*************** -*** 3340,3355 **** - void *tn_search_ctx; - } tagname_T; - -- /* -- * Array indexes used for cptext argument of ins_compl_add(). -- */ -- #define CPT_ABBR 0 /* "abbr" */ -- #define CPT_MENU 1 /* "menu" */ -- #define CPT_KIND 2 /* "kind" */ -- #define CPT_INFO 3 /* "info" */ -- #define CPT_USER_DATA 4 /* "user data" */ -- #define CPT_COUNT 5 /* Number of entries */ -- - typedef struct { - UINT32_T total[2]; - UINT32_T state[8]; ---- 3340,3345 ---- -*** ../vim-8.1.1075/src/tag.c 2019-03-22 17:03:01.779689390 +0100 ---- src/tag.c 2019-03-30 12:54:54.309407840 +0100 -*************** -*** 1591,1597 **** - #ifdef FEAT_INS_EXPAND - if ((flags & TAG_INS_COMP)) /* Double brackets for gcc */ - ins_compl_check_keys(30, FALSE); -! if (got_int || compl_interrupted) - #else - if (got_int) - #endif ---- 1591,1597 ---- - #ifdef FEAT_INS_EXPAND - if ((flags & TAG_INS_COMP)) /* Double brackets for gcc */ - ins_compl_check_keys(30, FALSE); -! if (got_int || ins_compl_interrupted()) - #else - if (got_int) - #endif -*** ../vim-8.1.1075/src/version.c 2019-03-30 12:51:18.626808012 +0100 ---- src/version.c 2019-03-30 13:00:27.254840750 +0100 -*************** -*** 777,778 **** ---- 777,780 ---- - { /* Add new patch number below this line */ -+ /**/ -+ 1076, - /**/ - --- -hundred-and-one symptoms of being an internet addict: -154. You fondle your mouse. - - /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ -/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ -\\\ an exciting new programming language -- http://www.Zimbu.org /// - \\\ help me help AIDS victims -- http://ICCF-Holland.org /// |