summaryrefslogtreecommitdiff
path: root/data/vim/patches/8.1.1076
diff options
context:
space:
mode:
Diffstat (limited to 'data/vim/patches/8.1.1076')
-rw-r--r--data/vim/patches/8.1.10769225
1 files changed, 9225 insertions, 0 deletions
diff --git a/data/vim/patches/8.1.1076 b/data/vim/patches/8.1.1076
new file mode 100644
index 000000000..4e18d4463
--- /dev/null
+++ b/data/vim/patches/8.1.1076
@@ -0,0 +1,9225 @@
+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,
+- &regmatch, 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,
++ &regmatch, 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 ///