From 7591bb39d58ece38a5fef984a08ea9012616c1f9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 30 Mar 2019 13:53:47 +0100 Subject: patch 8.1.1076: file for Insert mode is much too big Problem: File for Insert mode is much too big. Solution: Split off the code for Insert completion. (Yegappan Lakshmanan, closes #4044) --- Filelist | 2 + src/Make_bc5.mak | 1 + src/Make_cyg_ming.mak | 1 + src/Make_dice.mak | 4 + src/Make_ivc.mak | 5 + src/Make_manx.mak | 6 + src/Make_morph.mak | 1 + src/Make_mvc.mak | 4 + src/Make_sas.mak | 5 + src/Make_vms.mms | 21 +- src/Makefile | 10 + src/edit.c | 4334 +++-------------------------------------------- src/evalfunc.c | 2 +- src/globals.h | 4 - src/insexpand.c | 3992 +++++++++++++++++++++++++++++++++++++++++++ src/misc2.c | 1 + src/proto.h | 3 + src/proto/edit.pro | 19 +- src/proto/insexpand.pro | 50 + src/search.c | 4 +- src/spell.c | 2 +- src/structs.h | 40 +- src/tag.c | 2 +- src/version.c | 2 + 24 files changed, 4334 insertions(+), 4181 deletions(-) create mode 100644 src/insexpand.c create mode 100644 src/proto/insexpand.pro diff --git a/Filelist b/Filelist index 74b9312c60..7f1afd89ff 100644 --- a/Filelist +++ b/Filelist @@ -48,6 +48,7 @@ SRC_ALL = \ src/hardcopy.c \ src/hashtab.c \ src/indent.c \ + src/insexpand.c \ src/json.c \ src/json_test.c \ src/kword_test.c \ @@ -175,6 +176,7 @@ SRC_ALL = \ 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 \ diff --git a/src/Make_bc5.mak b/src/Make_bc5.mak index 7b5b0f2ccf..1c13931d31 100644 --- a/src/Make_bc5.mak +++ b/src/Make_bc5.mak @@ -534,6 +534,7 @@ vimobj = \ $(OBJDIR)\hardcopy.obj \ $(OBJDIR)\hashtab.obj \ $(OBJDIR)\indent.obj \ + $(OBJDIR)\insexpand.obj \ $(OBJDIR)\json.obj \ $(OBJDIR)\list.obj \ $(OBJDIR)\main.obj \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 98210c1f0e..e439de3517 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -721,6 +721,7 @@ OBJ = \ $(OUTDIR)/hardcopy.o \ $(OUTDIR)/hashtab.o \ $(OUTDIR)/indent.o \ + $(OUTDIR)/insexpand.o \ $(OUTDIR)/json.o \ $(OUTDIR)/list.o \ $(OUTDIR)/main.o \ diff --git a/src/Make_dice.mak b/src/Make_dice.mak index 2025974fc5..5bad80b850 100644 --- a/src/Make_dice.mak +++ b/src/Make_dice.mak @@ -51,6 +51,7 @@ SRC = \ hardcopy.c \ hashtab.c \ indent.c \ + insexpand.c \ json.c \ list.c \ main.c \ @@ -110,6 +111,7 @@ OBJ = o/arabic.o \ o/hardcopy.o \ o/hashtab.o \ o/indent.o \ + o/insexpand.o \ o/json.o \ o/list.o \ o/main.o \ @@ -213,6 +215,8 @@ o/hashtab.o: hashtab.c $(SYMS) o/indent.o: indent.c $(SYMS) +o/insexpand.o: insexpand.c $(SYMS) + o/json.o: json.c $(SYMS) o/list.o: list.c $(SYMS) diff --git a/src/Make_ivc.mak b/src/Make_ivc.mak index 0691e8dfc1..27b2c336d7 100644 --- a/src/Make_ivc.mak +++ b/src/Make_ivc.mak @@ -235,6 +235,7 @@ LINK32_OBJS= \ "$(INTDIR)/hardcopy.obj" \ "$(INTDIR)/hashtab.obj" \ "$(INTDIR)/indent.obj" \ + "$(INTDIR)/insexpand.obj" \ "$(INTDIR)/json.obj" \ "$(INTDIR)/list.obj" \ "$(INTDIR)/main.obj" \ @@ -439,6 +440,10 @@ SOURCE=.\hashtab.c SOURCE=.\indent.c # End Source File # Begin Source File +# +SOURCE=.\insexpand.c +# End Source File +# Begin Source File SOURCE=.\gui.c diff --git a/src/Make_manx.mak b/src/Make_manx.mak index 2cbc3df6aa..d150e4b6fc 100644 --- a/src/Make_manx.mak +++ b/src/Make_manx.mak @@ -61,6 +61,7 @@ SRC = arabic.c \ hardcopy.c \ hashtab.c \ indent.c \ + insexpand.c \ json.c \ list.c \ main.c \ @@ -122,6 +123,7 @@ OBJ = obj/arabic.o \ obj/hardcopy.o \ obj/hashtab.o \ obj/indent.o \ + obj/insexpand.o \ obj/json.o \ obj/list.o \ obj/main.o \ @@ -181,6 +183,7 @@ PRO = proto/arabic.pro \ proto/hardcopy.pro \ proto/hashtab.pro \ proto/indent.pro \ + proto/insexpand.pro \ proto/json.pro \ proto/list.pro \ proto/main.pro \ @@ -335,6 +338,9 @@ obj/hashtab.o: hashtab.c obj/indent.o: indent.c $(CCSYM) $@ indent.c +obj/insexpand.o: insexpand.c + $(CCSYM) $@ insexpand.c + obj/json.o: json.c $(CCSYM) $@ json.c diff --git a/src/Make_morph.mak b/src/Make_morph.mak index af231fb7b5..b224c67adc 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -49,6 +49,7 @@ SRC = arabic.c \ hardcopy.c \ hashtab.c \ indent.c \ + insexpand.c \ json.c \ list.c \ main.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index c29314c023..d71a3d3827 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -725,6 +725,7 @@ OBJ = \ $(OUTDIR)\hardcopy.obj \ $(OUTDIR)\hashtab.obj \ $(OUTDIR)\indent.obj \ + $(OUTDIR)\insexpand.obj \ $(OUTDIR)\json.obj \ $(OUTDIR)\list.obj \ $(OUTDIR)\main.obj \ @@ -1416,6 +1417,8 @@ $(OUTDIR)/hashtab.obj: $(OUTDIR) hashtab.c $(INCL) $(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,6 +1651,7 @@ proto.h: \ proto/hardcopy.pro \ proto/hashtab.pro \ proto/indent.pro \ + proto/insexpand.pro \ proto/json.pro \ proto/list.pro \ proto/main.pro \ diff --git a/src/Make_sas.mak b/src/Make_sas.mak index 6ba00fbd67..09650c31f6 100644 --- a/src/Make_sas.mak +++ b/src/Make_sas.mak @@ -114,6 +114,7 @@ SRC = \ hardcopy.c \ hashtab.c \ indent.c \ + insexpand.c \ json.c \ list.c \ main.c \ @@ -174,6 +175,7 @@ OBJ = \ hardcopy.o \ hashtab.o \ indent.o \ + insexpand.o \ json.o \ list.o \ main.o \ @@ -234,6 +236,7 @@ PRO = \ proto/hardcopy.pro \ proto/hashtab.pro \ proto/indent.pro \ + proto/insexpand.pro \ proto/json.pro \ proto/list.pro \ proto/main.pro \ @@ -373,6 +376,8 @@ hashtab.o: hashtab.c 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 diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 9159496fd3..bc8aee69f6 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -311,9 +311,9 @@ SRC = arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c charset.c \ 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 \ + 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,13 +325,13 @@ OBJ = arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj \ 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 \ + 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,6 +603,7 @@ if_mzsch.obj : if_mzsch.c vim.h [.auto]config.h feature.h os_unix.h \ 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 \ diff --git a/src/Makefile b/src/Makefile index 5fa37907fe..35471ae9b0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1599,6 +1599,7 @@ BASIC_SRC = \ if_cscope.c \ if_xcmdsrv.c \ indent.c \ + insexpand.c \ json.c \ list.c \ main.c \ @@ -1713,6 +1714,7 @@ OBJ_COMMON = \ objects/if_cscope.o \ objects/if_xcmdsrv.o \ objects/indent.o \ + objects/insexpand.o \ objects/list.o \ objects/mark.o \ objects/memline.o \ @@ -1844,6 +1846,7 @@ PRO_AUTO = \ if_ruby.pro \ if_xcmdsrv.pro \ indent.pro \ + insexpand.pro \ json.pro \ list.pro \ main.pro \ @@ -3098,6 +3101,9 @@ objects/if_tcl.o: if_tcl.c 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,6 +3506,10 @@ objects/indent.o: indent.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/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 \ diff --git a/src/edit.c b/src/edit.c index dcead23f54..0b5d0e8086 100644 --- a/src/edit.c +++ b/src/edit.c @@ -13,210 +13,18 @@ #include "vim.h" -#ifdef FEAT_INS_EXPAND -/* - * Definitions used for CTRL-X submode. - * Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] and - * ctrl_x_mode_names[]. - */ -# define CTRL_X_WANT_IDENT 0x100 - -# define CTRL_X_NORMAL 0 /* CTRL-N CTRL-P completion, default */ -# define CTRL_X_NOT_DEFINED_YET 1 -# define CTRL_X_SCROLL 2 -# define CTRL_X_WHOLE_LINE 3 -# define CTRL_X_FILES 4 -# define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT) -# define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT) -# define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT) -# define CTRL_X_FINISHED 8 -# define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT) -# define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT) -# define CTRL_X_CMDLINE 11 -# define CTRL_X_FUNCTION 12 -# define CTRL_X_OMNI 13 -# define CTRL_X_SPELL 14 -# define CTRL_X_LOCAL_MSG 15 /* only used in "ctrl_x_msgs" */ -# define CTRL_X_EVAL 16 /* for builtin function complete() */ - -# define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] -# define CTRL_X_MODE_LINE_OR_EVAL(m) ((m) == CTRL_X_WHOLE_LINE || (m) == CTRL_X_EVAL) - -// Message for CTRL-X mode, index is ctrl_x_mode. -static char *ctrl_x_msgs[] = -{ - N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl. - N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), - NULL, // CTRL_X_SCROLL: depends on state - N_(" Whole line completion (^L^N^P)"), - N_(" File name completion (^F^N^P)"), - N_(" Tag completion (^]^N^P)"), - N_(" Path pattern completion (^N^P)"), - N_(" Definition completion (^D^N^P)"), - NULL, // CTRL_X_FINISHED - N_(" Dictionary completion (^K^N^P)"), - N_(" Thesaurus completion (^T^N^P)"), - N_(" Command-line completion (^V^N^P)"), - N_(" User defined completion (^U^N^P)"), - N_(" Omni completion (^O^N^P)"), - N_(" Spelling suggestion (s^N^P)"), - N_(" Keyword Local completion (^N^P)"), - NULL, // CTRL_X_EVAL doesn't use msg. -}; - -static char *ctrl_x_mode_names[] = { - "keyword", - "ctrl_x", - "unknown", // CTRL_X_SCROLL - "whole_line", - "files", - "tags", - "path_patterns", - "path_defines", - "unknown", // CTRL_X_FINISHED - "dictionary", - "thesaurus", - "cmdline", - "function", - "omni", - "spell", - NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" - "eval" - }; - - -static char e_hitend[] = N_("Hit end of paragraph"); -# ifdef FEAT_COMPL_FUNC -static char e_complwin[] = N_("E839: Completion function changed window"); -static char e_compldel[] = N_("E840: Completion function deleted text"); -# endif - -/* - * Structure used to store one match for insert completion. - */ -typedef struct compl_S compl_T; -struct compl_S -{ - compl_T *cp_next; - compl_T *cp_prev; - char_u *cp_str; /* matched text */ - char cp_icase; /* TRUE or FALSE: ignore case */ - char_u *(cp_text[CPT_COUNT]); /* text for the menu */ - char_u *cp_fname; /* file containing the match, allocated when - * cp_flags has FREE_FNAME */ - int cp_flags; /* ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME */ - int cp_number; /* sequence number */ -}; - -# define ORIGINAL_TEXT (1) /* the original text when the expansion begun */ -# define FREE_FNAME (2) - -/* - * All the current matches are stored in a list. - * "compl_first_match" points to the start of the list. - * "compl_curr_match" points to the currently selected entry. - * "compl_shown_match" is different from compl_curr_match during - * ins_compl_get_exp(). - */ -static compl_T *compl_first_match = NULL; -static compl_T *compl_curr_match = NULL; -static compl_T *compl_shown_match = NULL; -static compl_T *compl_old_match = NULL; - -/* After using a cursor key selects a match in the popup menu, - * otherwise it inserts a line break. */ -static int compl_enter_selects = FALSE; - -/* When "compl_leader" is not NULL only matches that start with this string - * are used. */ -static char_u *compl_leader = NULL; - -static int compl_get_longest = FALSE; /* put longest common string - in compl_leader */ - -static int compl_no_insert = FALSE; /* FALSE: select & insert - TRUE: noinsert */ -static int compl_no_select = FALSE; /* FALSE: select & insert - TRUE: noselect */ - -static int compl_used_match; /* Selected one of the matches. When - FALSE the match was edited or using - the longest common string. */ - -static int compl_was_interrupted = FALSE; /* didn't finish finding - completions. */ - -static int compl_restarting = FALSE; /* don't insert match */ - -/* When the first completion is done "compl_started" is set. When it's - * FALSE the word to be completed must be located. */ -static int compl_started = FALSE; - -/* Which Ctrl-X mode are we in? */ -static int ctrl_x_mode = CTRL_X_NORMAL; +#define BACKSPACE_CHAR 1 +#define BACKSPACE_WORD 2 +#define BACKSPACE_WORD_NOT_SPACE 3 +#define BACKSPACE_LINE 4 +#ifdef FEAT_INS_EXPAND /* Set when doing something for completion that may call edit() recursively, * which is not allowed. */ -static int compl_busy = FALSE; - -static int compl_matches = 0; -static char_u *compl_pattern = NULL; -static int compl_direction = FORWARD; -static int compl_shows_dir = FORWARD; -static int compl_pending = 0; /* > 1 for postponed CTRL-N */ -static pos_T compl_startpos; -static colnr_T compl_col = 0; /* column where the text starts - * that is being completed */ -static char_u *compl_orig_text = NULL; /* text as it was before - * completion started */ -static int compl_cont_mode = 0; -static expand_T compl_xp; - -static int compl_opt_refresh_always = FALSE; -static int compl_opt_suppress_empty = FALSE; - -static void ins_ctrl_x(void); -static int has_compl_option(int dict_opt); -static int ins_compl_accept_char(int c); -static int ins_compl_add(char_u *str, int len, int icase, char_u *fname, char_u **cptext, int cdir, int flags, int adup); -static void ins_compl_longest_match(compl_T *match); -static void ins_compl_del_pum(void); -static int pum_wanted(void); -static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir); -static char_u *find_line_end(char_u *ptr); -static void ins_compl_free(void); -static void ins_compl_clear(void); -static char_u *ins_compl_mode(void); -static int ins_compl_bs(void); -static int ins_compl_need_restart(void); -static void ins_compl_new_leader(void); -static void ins_compl_addleader(int c); -static int ins_compl_len(void); -static void ins_compl_restart(void); -static void ins_compl_set_original_text(char_u *str); -static void ins_compl_addfrommatch(void); -static int ins_compl_prep(int c); -static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg); -# if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) -static void ins_compl_add_list(list_T *list); -static void ins_compl_add_dict(dict_T *dict); -# endif -static void ins_compl_delete(void); -static void ins_compl_insert(int in_compl_func); -static int ins_compl_key2dir(int c); -static int ins_compl_pum_key(int c); -static int ins_compl_key2count(int c); -static int ins_complete(int c, int enable_pum); -static void show_pum(int prev_w_wrow, int prev_w_leftcol); -static unsigned quote_meta(char_u *dest, char_u *str, int len); +static int compl_busy = FALSE; #endif /* FEAT_INS_EXPAND */ -#define BACKSPACE_CHAR 1 -#define BACKSPACE_WORD 2 -#define BACKSPACE_WORD_NOT_SPACE 3 -#define BACKSPACE_LINE 4 -static void ins_redraw(int ready); static void ins_ctrl_v(void); #ifdef FEAT_JOB_CHANNEL static void init_prompt(int cmdchar_todo); @@ -226,12 +34,9 @@ static void insert_special(int, int, int); static void internal_format(int textwidth, int second_indent, int flags, int format_only, int c); static void check_auto_format(int); static void redo_literal(int c); -static void start_arrow(pos_T *end_insert_pos); static void start_arrow_common(pos_T *end_insert_pos, int change); #ifdef FEAT_SPELL static void check_spell_redraw(void); -static void spell_back_to_badword(void); -static int spell_bad_len = 0; /* length of located bad word */ #endif static void stop_insert(pos_T *end_insert_pos, int esc, int nomove); static int echeck_abbr(int); @@ -274,7 +79,6 @@ static void ins_pagedown(void); static void ins_drop(void); #endif static int ins_tab(void); -static int ins_eol(int c); #ifdef FEAT_DIGRAPHS static int ins_digraph(void); #endif @@ -285,7 +89,6 @@ static void ins_try_si(int c); #if defined(FEAT_EVAL) static char_u *do_insert_char_pre(int c); #endif -static int ins_apply_autocmds(event_T event); static colnr_T Insstart_textlen; /* length of line when insert started */ static colnr_T Insstart_blank_vcol; /* vcol for first inserted blank */ @@ -393,7 +196,7 @@ edit( #ifdef FEAT_INS_EXPAND /* Don't allow recursive insert mode when busy with completion. */ - if (compl_started || compl_busy || pum_visible()) + if (ins_compl_active() || compl_busy || pum_visible()) { emsg(_(e_secure)); return FALSE; @@ -834,28 +637,26 @@ edit( * and the cursor is still in the completed word. Only when there is * a match, skip this when no matches were found. */ - if (compl_started + if (ins_compl_active() && pum_wanted() - && curwin->w_cursor.col >= compl_col - && (compl_shown_match == NULL - || compl_shown_match != compl_shown_match->cp_next)) + && curwin->w_cursor.col >= ins_compl_col() + && ins_compl_has_shown_match()) { /* BS: Delete one character from "compl_leader". */ if ((c == K_BS || c == Ctrl_H) - && curwin->w_cursor.col > compl_col + && curwin->w_cursor.col > ins_compl_col() && (c = ins_compl_bs()) == NUL) continue; /* When no match was selected or it was edited. */ - if (!compl_used_match) + if (!ins_compl_used_match()) { /* CTRL-L: Add one character from the current match to * "compl_leader". Except when at the original match and * there is nothing to add, CTRL-L works like CTRL-P then. */ if (c == Ctrl_L - && (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode) - || (int)STRLEN(compl_shown_match->cp_str) - > curwin->w_cursor.col - compl_col)) + && (!ctrl_x_mode_line_or_eval() + || ins_compl_long_shown_match())) { ins_compl_addfrommatch(); continue; @@ -883,8 +684,9 @@ edit( } /* Pressing CTRL-Y selects the current match. When - * compl_enter_selects is set the Enter key does the same. */ - if ((c == Ctrl_Y || (compl_enter_selects + * ins_compl_enter_selects() is set the Enter key does the + * same. */ + if ((c == Ctrl_Y || (ins_compl_enter_selects() && (c == CAR || c == K_KENTER || c == NL))) && stop_arrow() == OK) { @@ -896,7 +698,7 @@ edit( /* Prepare for or stop CTRL-X mode. This doesn't do completion, but * it does fix up the text when finishing completion. */ - compl_get_longest = FALSE; + ins_compl_init_get_longest(); if (ins_compl_prep(c)) continue; #endif @@ -939,7 +741,7 @@ edit( #endif #ifdef FEAT_INS_EXPAND - if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) + if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode_cmdline()) goto docomplete; #endif if (c == Ctrl_V || c == Ctrl_Q) @@ -952,7 +754,7 @@ edit( #ifdef FEAT_CINDENT if (cindent_on() # ifdef FEAT_INS_EXPAND - && ctrl_x_mode == 0 + && ctrl_x_mode_none() # endif ) { @@ -1073,7 +875,7 @@ doESCkey: case Ctrl_O: /* execute one command */ #ifdef FEAT_COMPL_FUNC - if (ctrl_x_mode == CTRL_X_OMNI) + if (ctrl_x_mode_omni()) goto docomplete; #endif if (echeck_abbr(Ctrl_O + ABBR_OFF)) @@ -1149,14 +951,14 @@ doESCkey: case Ctrl_D: /* Make indent one shiftwidth smaller. */ #if defined(FEAT_INS_EXPAND) && defined(FEAT_FIND_ID) - if (ctrl_x_mode == CTRL_X_PATH_DEFINES) + if (ctrl_x_mode_path_defines()) goto docomplete; #endif /* FALLTHROUGH */ case Ctrl_T: /* Make indent one shiftwidth greater. */ # ifdef FEAT_INS_EXPAND - if (c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) + if (c == Ctrl_T && ctrl_x_mode_thesaurus()) { if (has_compl_option(FALSE)) goto docomplete; @@ -1200,7 +1002,7 @@ doESCkey: case Ctrl_U: /* delete all inserted text in current line */ # ifdef FEAT_COMPL_FUNC /* CTRL-X CTRL-U completes with 'completefunc'. */ - if (ctrl_x_mode == CTRL_X_FUNCTION) + if (ctrl_x_mode_function()) goto docomplete; # endif did_backspace = ins_bs(c, BACKSPACE_LINE, &inserted_space); @@ -1382,7 +1184,7 @@ doESCkey: case TAB: /* TAB or Complete patterns along path */ #if defined(FEAT_INS_EXPAND) && defined(FEAT_FIND_ID) - if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) + if (ctrl_x_mode_path_patterns()) goto docomplete; #endif inserted_space = FALSE; @@ -1436,7 +1238,7 @@ doESCkey: #if defined(FEAT_DIGRAPHS) || defined(FEAT_INS_EXPAND) case Ctrl_K: /* digraph or keyword completion */ # ifdef FEAT_INS_EXPAND - if (ctrl_x_mode == CTRL_X_DICTIONARY) + if (ctrl_x_mode_dictionary()) { if (has_compl_option(TRUE)) goto docomplete; @@ -1457,25 +1259,25 @@ doESCkey: break; case Ctrl_RSB: /* Tag name completion after ^X */ - if (ctrl_x_mode != CTRL_X_TAGS) + if (!ctrl_x_mode_tags()) goto normalchar; goto docomplete; case Ctrl_F: /* File name completion after ^X */ - if (ctrl_x_mode != CTRL_X_FILES) + if (!ctrl_x_mode_files()) goto normalchar; goto docomplete; case 's': /* Spelling completion after ^X */ case Ctrl_S: - if (ctrl_x_mode != CTRL_X_SPELL) + if (!ctrl_x_mode_spell()) goto normalchar; goto docomplete; #endif case Ctrl_L: /* Whole line completion after ^X */ #ifdef FEAT_INS_EXPAND - if (ctrl_x_mode != CTRL_X_WHOLE_LINE) + if (!ctrl_x_mode_whole_line()) #endif { /* CTRL-L with 'insertmode' set: Leave Insert mode */ @@ -1495,8 +1297,7 @@ doESCkey: /* if 'complete' is empty then plain ^P is no longer special, * but it is under other ^X modes */ if (*curbuf->b_p_cpt == NUL - && (ctrl_x_mode == CTRL_X_NORMAL - || ctrl_x_mode == CTRL_X_WHOLE_LINE) + && (ctrl_x_mode_normal() || ctrl_x_mode_whole_line()) && !(compl_cont_status & CONT_LOCAL)) goto normalchar; @@ -1608,7 +1409,7 @@ normalchar: if (c != K_CURSORHOLD #ifdef FEAT_COMPL_FUNC /* but not in CTRL-X mode, a script can't restore the state */ - && ctrl_x_mode == CTRL_X_NORMAL + && ctrl_x_mode_normal() #endif ) did_cursorhold = FALSE; @@ -1620,7 +1421,7 @@ normalchar: #ifdef FEAT_CINDENT if (can_cindent && cindent_on() # ifdef FEAT_INS_EXPAND - && ctrl_x_mode == CTRL_X_NORMAL + && ctrl_x_mode_normal() # endif ) { @@ -1641,6 +1442,12 @@ force_cindent: /* NOTREACHED */ } + int +ins_need_undo_get(void) +{ + return ins_need_undo; +} + /* * Redraw for Insert mode. * This is postponed until getting the next character to make '$' in the 'cpo' @@ -1648,7 +1455,7 @@ force_cindent: * Only redraw when there are no characters available. This speeds up * inserting sequences of characters (e.g., for CTRL-R). */ - static void + void ins_redraw( int ready UNUSED) /* not busy with something */ { @@ -2107,3924 +1914,211 @@ change_indent( { /* * Compute the screen column where the cursor should be. - */ - vcol = get_indent() - vcol; - curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); - - /* - * Advance the cursor until we reach the right screen column. - */ - vcol = last_vcol = 0; - new_cursor_col = -1; - ptr = ml_get_curline(); - while (vcol <= (int)curwin->w_virtcol) - { - last_vcol = vcol; - if (has_mbyte && new_cursor_col >= 0) - new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col); - else - ++new_cursor_col; - vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol); - } - vcol = last_vcol; - - /* - * May need to insert spaces to be able to position the cursor on - * the right screen column. - */ - if (vcol != (int)curwin->w_virtcol) - { - curwin->w_cursor.col = (colnr_T)new_cursor_col; - i = (int)curwin->w_virtcol - vcol; - ptr = alloc((unsigned)(i + 1)); - if (ptr != NULL) - { - new_cursor_col += i; - ptr[i] = NUL; - while (--i >= 0) - ptr[i] = ' '; - ins_str(ptr); - vim_free(ptr); - } - } - - /* - * When changing the indent while the cursor is in it, reset - * Insstart_col to 0. - */ - insstart_less = MAXCOL; - } - - curwin->w_p_list = save_p_list; - - if (new_cursor_col <= 0) - curwin->w_cursor.col = 0; - else - curwin->w_cursor.col = (colnr_T)new_cursor_col; - curwin->w_set_curswant = TRUE; - changed_cline_bef_curs(); - - /* - * May have to adjust the start of the insert. - */ - if (State & INSERT) - { - if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) - { - if ((int)Insstart.col <= insstart_less) - Insstart.col = 0; - else - Insstart.col -= insstart_less; - } - if ((int)ai_col <= insstart_less) - ai_col = 0; - else - ai_col -= insstart_less; - } - - /* - * For REPLACE mode, may have to fix the replace stack, if it's possible. - * If the number of characters before the cursor decreased, need to pop a - * few characters from the replace stack. - * If the number of characters before the cursor increased, need to push a - * few NULs onto the replace stack. - */ - if (REPLACE_NORMAL(State) && start_col >= 0) - { - while (start_col > (int)curwin->w_cursor.col) - { - replace_join(0); /* remove a NUL from the replace stack */ - --start_col; - } - while (start_col < (int)curwin->w_cursor.col || replaced) - { - replace_push(NUL); - if (replaced) - { - replace_push(replaced); - replaced = NUL; - } - ++start_col; - } - } - - /* - * For VREPLACE mode, we also have to fix the replace stack. In this case - * it is always possible because we backspace over the whole line and then - * put it back again the way we wanted it. - */ - if (State & VREPLACE_FLAG) - { - /* If orig_line didn't allocate, just return. At least we did the job, - * even if you can't backspace. */ - if (orig_line == NULL) - return; - - /* Save new line */ - new_line = vim_strsave(ml_get_curline()); - if (new_line == NULL) - return; - - /* We only put back the new line up to the cursor */ - new_line[curwin->w_cursor.col] = NUL; - - /* Put back original line */ - ml_replace(curwin->w_cursor.lnum, orig_line, FALSE); - curwin->w_cursor.col = orig_col; - - /* Backspace from cursor to start of line */ - backspace_until_column(0); - - /* Insert new stuff into line again */ - ins_bytes(new_line); - - vim_free(new_line); - } -} - -/* - * Truncate the space at the end of a line. This is to be used only in an - * insert mode. It handles fixing the replace stack for REPLACE and VREPLACE - * modes. - */ - void -truncate_spaces(char_u *line) -{ - int i; - - /* find start of trailing white space */ - for (i = (int)STRLEN(line) - 1; i >= 0 && VIM_ISWHITE(line[i]); i--) - { - if (State & REPLACE_FLAG) - replace_join(0); /* remove a NUL from the replace stack */ - } - line[i + 1] = NUL; -} - -/* - * Backspace the cursor until the given column. Handles REPLACE and VREPLACE - * modes correctly. May also be used when not in insert mode at all. - * Will attempt not to go before "col" even when there is a composing - * character. - */ - void -backspace_until_column(int col) -{ - while ((int)curwin->w_cursor.col > col) - { - curwin->w_cursor.col--; - if (State & REPLACE_FLAG) - replace_do_bs(col); - else if (!del_char_after_col(col)) - break; - } -} - -/* - * Like del_char(), but make sure not to go before column "limit_col". - * Only matters when there are composing characters. - * Return TRUE when something was deleted. - */ - static int -del_char_after_col(int limit_col UNUSED) -{ - if (enc_utf8 && limit_col >= 0) - { - colnr_T ecol = curwin->w_cursor.col + 1; - - /* Make sure the cursor is at the start of a character, but - * skip forward again when going too far back because of a - * composing character. */ - mb_adjust_cursor(); - while (curwin->w_cursor.col < (colnr_T)limit_col) - { - int l = utf_ptr2len(ml_get_cursor()); - - if (l == 0) /* end of line */ - break; - curwin->w_cursor.col += l; - } - if (*ml_get_cursor() == NUL || curwin->w_cursor.col == ecol) - return FALSE; - del_bytes((long)((int)ecol - curwin->w_cursor.col), FALSE, TRUE); - } - else - (void)del_char(FALSE); - 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 and 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/" expands files in - * "proto", not "proto/" as a whole */ - return vim_isfilec(c) && !vim_ispathsep(c); - - case CTRL_X_CMDLINE: - case CTRL_X_OMNI: - /* Command line and Omni completion can work with just about any - * printable character, but do stop at white space. */ - return vim_isprintc(c) && !VIM_ISWHITE(c); - - case CTRL_X_WHOLE_LINE: - /* For while line completion a space can be part of the line. */ - return vim_isprintc(c); - } - return vim_iswordc(c); -} - -/* - * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the - * case of the originally typed text is used, and the case of the completed - * text is inferred, ie this tries to work out what case you probably wanted - * the rest of the word to be in -- webb - */ - int -ins_compl_add_infercase( - char_u *str, - int len, - int icase, - char_u *fname, - int dir, - int flags) -{ - char_u *p; - int i, c; - int actual_len; /* Take multi-byte characters */ - int actual_compl_length; /* into account. */ - int min_len; - int *wca; /* Wide character array. */ - int has_lower = FALSE; - int was_letter = FALSE; - - if (p_ic && curbuf->b_p_inf && len > 0) - { - /* Infer case of completed part. */ - - /* Find actual length of completion. */ - if (has_mbyte) - { - p = str; - actual_len = 0; - while (*p != NUL) - { - MB_PTR_ADV(p); - ++actual_len; - } - } - else - actual_len = len; - - /* Find actual length of original text. */ - if (has_mbyte) - { - p = compl_orig_text; - actual_compl_length = 0; - while (*p != NUL) - { - MB_PTR_ADV(p); - ++actual_compl_length; - } - } - else - actual_compl_length = compl_length; - - /* "actual_len" may be smaller than "actual_compl_length" when using - * thesaurus, only use the minimum when comparing. */ - min_len = actual_len < actual_compl_length - ? actual_len : actual_compl_length; - - /* Allocate wide character array for the completion and fill it. */ - wca = (int *)alloc((unsigned)(actual_len * sizeof(int))); - if (wca != NULL) - { - p = str; - for (i = 0; i < actual_len; ++i) - if (has_mbyte) - wca[i] = mb_ptr2char_adv(&p); - else - wca[i] = *(p++); - - /* Rule 1: Were any chars converted to lower? */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (MB_ISLOWER(c)) - { - has_lower = TRUE; - if (MB_ISUPPER(wca[i])) - { - /* Rule 1 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = MB_TOLOWER(wca[i]); - break; - } - } - } - - /* - * Rule 2: No lower case, 2nd consecutive letter converted to - * upper case. - */ - if (!has_lower) - { - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i])) - { - /* Rule 2 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = MB_TOUPPER(wca[i]); - break; - } - was_letter = MB_ISLOWER(c) || MB_ISUPPER(c); - } - } - - /* Copy the original case of the part we typed. */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (MB_ISLOWER(c)) - wca[i] = MB_TOLOWER(wca[i]); - else if (MB_ISUPPER(c)) - wca[i] = MB_TOUPPER(wca[i]); - } - - /* - * Generate encoding specific output from wide character array. - * Multi-byte characters can occupy up to five bytes more than - * ASCII characters, and we also need one byte for NUL, so stay - * six bytes away from the edge of IObuff. - */ - p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) - if (has_mbyte) - p += (*mb_char2bytes)(wca[i++], p); - else - *(p++) = wca[i++]; - *p = NUL; - - vim_free(wca); - } - - return ins_compl_add(IObuff, len, icase, fname, NULL, dir, - flags, FALSE); - } - return ins_compl_add(str, len, icase, fname, NULL, dir, flags, FALSE); -} - -/* - * Add a match to the list of matches. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error, - * maybe because alloc() returns NULL, then FAIL is returned. - */ - static int -ins_compl_add( - char_u *str, - int len, - int icase, - char_u *fname, - char_u **cptext, /* extra text for popup menu or NULL */ - int cdir, - int flags, - int adup) /* accept duplicate match */ -{ - compl_T *match; - int dir = (cdir == 0 ? compl_direction : cdir); - - ui_breakcheck(); - if (got_int) - return FAIL; - if (len < 0) - len = (int)STRLEN(str); - - /* - * If the same match is already present, don't add it. - */ - if (compl_first_match != NULL && !adup) - { - match = compl_first_match; - do - { - if ( !(match->cp_flags & ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) - return NOTDONE; - match = match->cp_next; - } while (match != NULL && match != compl_first_match); - } - - /* Remove any popup menu before changing the list of matches. */ - ins_compl_del_pum(); - - /* - * Allocate a new match structure. - * Copy the values to the new match structure. - */ - match = (compl_T *)alloc_clear((unsigned)sizeof(compl_T)); - if (match == NULL) - return FAIL; - match->cp_number = -1; - if (flags & ORIGINAL_TEXT) - match->cp_number = 0; - if ((match->cp_str = vim_strnsave(str, len)) == NULL) - { - vim_free(match); - return FAIL; - } - match->cp_icase = icase; - - /* match-fname is: - * - compl_curr_match->cp_fname if it is a string equal to fname. - * - a copy of fname, FREE_FNAME is set to free later THE allocated mem. - * - NULL otherwise. --Acevedo */ - if (fname != NULL - && compl_curr_match != NULL - && compl_curr_match->cp_fname != NULL - && STRCMP(fname, compl_curr_match->cp_fname) == 0) - match->cp_fname = compl_curr_match->cp_fname; - else if (fname != NULL) - { - match->cp_fname = vim_strsave(fname); - flags |= FREE_FNAME; - } - else - match->cp_fname = NULL; - match->cp_flags = flags; - - if (cptext != NULL) - { - int i; - - for (i = 0; i < CPT_COUNT; ++i) - if (cptext[i] != NULL && *cptext[i] != NUL) - match->cp_text[i] = vim_strsave(cptext[i]); - } - - /* - * Link the new match structure in the list of matches. - */ - if (compl_first_match == NULL) - match->cp_next = match->cp_prev = NULL; - else if (dir == FORWARD) - { - match->cp_next = compl_curr_match->cp_next; - match->cp_prev = compl_curr_match; - } - else /* BACKWARD */ - { - match->cp_next = compl_curr_match; - match->cp_prev = compl_curr_match->cp_prev; - } - if (match->cp_next) - match->cp_next->cp_prev = match; - if (match->cp_prev) - match->cp_prev->cp_next = match; - else /* if there's nothing before, it is the first match */ - compl_first_match = match; - compl_curr_match = match; - - /* - * Find the longest common string if still doing that. - */ - if (compl_get_longest && (flags & ORIGINAL_TEXT) == 0) - ins_compl_longest_match(match); - - return OK; -} - -/* - * Return TRUE if "str[len]" matches with match->cp_str, considering - * match->cp_icase. - */ - static int -ins_compl_equal(compl_T *match, char_u *str, int len) -{ - if (match->cp_icase) - return STRNICMP(match->cp_str, str, (size_t)len) == 0; - return STRNCMP(match->cp_str, str, (size_t)len) == 0; -} - -/* - * Reduce the longest common string for match "match". - */ - static void -ins_compl_longest_match(compl_T *match) -{ - char_u *p, *s; - int c1, c2; - int had_match; - - if (compl_leader == NULL) - { - /* First match, use it as a whole. */ - compl_leader = vim_strsave(match->cp_str); - if (compl_leader != NULL) - { - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) - ins_compl_delete(); - compl_used_match = FALSE; - } - } - else - { - /* Reduce the text if this match differs from compl_leader. */ - p = compl_leader; - s = match->cp_str; - while (*p != NUL) - { - if (has_mbyte) - { - c1 = mb_ptr2char(p); - c2 = mb_ptr2char(s); - } - else - { - c1 = *p; - c2 = *s; - } - if (match->cp_icase ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) - : (c1 != c2)) - break; - if (has_mbyte) - { - MB_PTR_ADV(p); - MB_PTR_ADV(s); - } - else - { - ++p; - ++s; - } - } - - if (*p != NUL) - { - /* Leader was shortened, need to change the inserted text. */ - *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) - ins_compl_delete(); - } - - compl_used_match = FALSE; - } -} - -/* - * Add an array of matches to the list of matches. - * Frees matches[]. - */ - static void -ins_compl_add_matches( - int num_matches, - char_u **matches, - int icase) -{ - int i; - int add_r = OK; - int dir = compl_direction; - - for (i = 0; i < num_matches && add_r != FAIL; i++) - if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, dir, 0, FALSE)) == OK) - /* if dir was BACKWARD then honor it just once */ - dir = FORWARD; - FreeWild(num_matches, matches); -} - -/* Make the completion list cyclic. - * Return the number of matches (excluding the original). - */ - static int -ins_compl_make_cyclic(void) -{ - compl_T *match; - int count = 0; - - if (compl_first_match != NULL) - { - /* - * Find the end of the list. - */ - match = compl_first_match; - /* there's always an entry for the compl_orig_text, it doesn't count. */ - while (match->cp_next != NULL && match->cp_next != compl_first_match) - { - match = match->cp_next; - ++count; - } - match->cp_next = compl_first_match; - compl_first_match->cp_prev = match; - } - return count; -} - -/* - * Set variables that store noselect and noinsert behavior from the - * 'completeopt' value. - */ - void -completeopt_was_set(void) -{ - compl_no_insert = FALSE; - compl_no_select = FALSE; - if (strstr((char *)p_cot, "noselect") != NULL) - compl_no_select = TRUE; - if (strstr((char *)p_cot, "noinsert") != NULL) - compl_no_insert = TRUE; -} - -/* - * Start completion for the complete() function. - * "startcol" is where the matched text starts (1 is first column). - * "list" is the list of matches. - */ - void -set_completion(colnr_T startcol, list_T *list) -{ - int save_w_wrow = curwin->w_wrow; - int save_w_leftcol = curwin->w_leftcol; - - /* If already doing completions stop it. */ - if (ctrl_x_mode != CTRL_X_NORMAL) - ins_compl_prep(' '); - ins_compl_clear(); - ins_compl_free(); - - compl_direction = FORWARD; - if (startcol > curwin->w_cursor.col) - startcol = curwin->w_cursor.col; - compl_col = startcol; - compl_length = (int)curwin->w_cursor.col - (int)startcol; - /* compl_pattern doesn't need to be set */ - compl_orig_text = vim_strnsave(ml_get_curline() + compl_col, compl_length); - if (compl_orig_text == NULL || ins_compl_add(compl_orig_text, - -1, p_ic, NULL, NULL, 0, ORIGINAL_TEXT, FALSE) != OK) - return; - - ctrl_x_mode = CTRL_X_EVAL; - - ins_compl_add_list(list); - compl_matches = ins_compl_make_cyclic(); - compl_started = TRUE; - compl_used_match = TRUE; - compl_cont_status = 0; - - compl_curr_match = compl_first_match; - if (compl_no_insert || compl_no_select) - { - ins_complete(K_DOWN, FALSE); - if (compl_no_select) - /* Down/Up has no real effect. */ - ins_complete(K_UP, FALSE); - } - else - ins_complete(Ctrl_N, FALSE); - compl_enter_selects = compl_no_insert; - - /* Lazily show the popup menu, unless we got interrupted. */ - if (!compl_interrupted) - show_pum(save_w_wrow, save_w_leftcol); - out_flush(); -} - - -/* "compl_match_array" points the currently displayed list of entries in the - * popup menu. It is NULL when there is no popup menu. */ -static pumitem_T *compl_match_array = NULL; -static int compl_match_arraysize; - -/* - * Update the screen and when there is any scrolling remove the popup menu. - */ - static void -ins_compl_upd_pum(void) -{ - int h; - - if (compl_match_array != NULL) - { - h = curwin->w_cline_height; - // Update the screen later, before drawing the popup menu over it. - pum_call_update_screen(); - if (h != curwin->w_cline_height) - ins_compl_del_pum(); - } -} - -/* - * Remove any popup menu. - */ - static void -ins_compl_del_pum(void) -{ - if (compl_match_array != NULL) - { - pum_undisplay(); - VIM_CLEAR(compl_match_array); - } -} - -/* - * Return TRUE if the popup menu should be displayed. - */ - static int -pum_wanted(void) -{ - /* 'completeopt' must contain "menu" or "menuone" */ - if (vim_strchr(p_cot, 'm') == NULL) - return FALSE; - - /* The display looks bad on a B&W display. */ - if (t_colors < 8 -#ifdef FEAT_GUI - && !gui.in_use -#endif - ) - return FALSE; - return TRUE; -} - -/* - * Return TRUE if there are two or more matches to be shown in the popup menu. - * One if 'completopt' contains "menuone". - */ - static int -pum_enough_matches(void) -{ - compl_T *compl; - int i; - - /* Don't display the popup menu if there are no matches or there is only - * one (ignoring the original text). */ - compl = compl_first_match; - i = 0; - do - { - if (compl == NULL - || ((compl->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) - break; - compl = compl->cp_next; - } while (compl != compl_first_match); - - if (strstr((char *)p_cot, "menuone") != NULL) - return (i >= 1); - return (i >= 2); -} - -/* - * Show the popup menu for the list of matches. - * Also adjusts "compl_shown_match" to an entry that is actually displayed. - */ - void -ins_compl_show_pum(void) -{ - compl_T *compl; - compl_T *shown_compl = NULL; - int did_find_shown_match = FALSE; - int shown_match_ok = FALSE; - int i; - int cur = -1; - colnr_T col; - int lead_len = 0; - - if (!pum_wanted() || !pum_enough_matches()) - return; - -#if defined(FEAT_EVAL) - /* Dirty hard-coded hack: remove any matchparen highlighting. */ - do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|3match none|endif"); -#endif - - // Update the screen later, before drawing the popup menu over it. - pum_call_update_screen(); - - if (compl_match_array == NULL) - { - /* Need to build the popup menu list. */ - compl_match_arraysize = 0; - compl = compl_first_match; - if (compl_leader != NULL) - lead_len = (int)STRLEN(compl_leader); - do - { - if ((compl->cp_flags & ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) - ++compl_match_arraysize; - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - if (compl_match_arraysize == 0) - return; - compl_match_array = (pumitem_T *)alloc_clear( - (unsigned)(sizeof(pumitem_T) - * compl_match_arraysize)); - if (compl_match_array != NULL) - { - /* If the current match is the original text don't find the first - * match after it, don't highlight anything. */ - if (compl_shown_match->cp_flags & ORIGINAL_TEXT) - shown_match_ok = TRUE; - - i = 0; - compl = compl_first_match; - do - { - if ((compl->cp_flags & ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) - { - if (!shown_match_ok) - { - if (compl == compl_shown_match || did_find_shown_match) - { - /* This item is the shown match or this is the - * first displayed item after the shown match. */ - compl_shown_match = compl; - did_find_shown_match = TRUE; - shown_match_ok = TRUE; - } - else - /* Remember this displayed match for when the - * shown match is just below it. */ - shown_compl = compl; - cur = i; - } - - if (compl->cp_text[CPT_ABBR] != NULL) - compl_match_array[i].pum_text = - compl->cp_text[CPT_ABBR]; - else - compl_match_array[i].pum_text = compl->cp_str; - compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; - compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; - if (compl->cp_text[CPT_MENU] != NULL) - compl_match_array[i++].pum_extra = - compl->cp_text[CPT_MENU]; - else - compl_match_array[i++].pum_extra = compl->cp_fname; - } - - if (compl == compl_shown_match) - { - did_find_shown_match = TRUE; - - /* When the original text is the shown match don't set - * compl_shown_match. */ - if (compl->cp_flags & ORIGINAL_TEXT) - shown_match_ok = TRUE; - - if (!shown_match_ok && shown_compl != NULL) - { - /* The shown match isn't displayed, set it to the - * previously displayed match. */ - compl_shown_match = shown_compl; - shown_match_ok = TRUE; - } - } - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - - if (!shown_match_ok) /* no displayed match at all */ - cur = -1; - } - } - else - { - /* popup menu already exists, only need to find the current item.*/ - for (i = 0; i < compl_match_arraysize; ++i) - if (compl_match_array[i].pum_text == compl_shown_match->cp_str - || compl_match_array[i].pum_text - == compl_shown_match->cp_text[CPT_ABBR]) - { - cur = i; - break; - } - } - - if (compl_match_array != NULL) - { - /* In Replace mode when a $ is displayed at the end of the line only - * part of the screen would be updated. We do need to redraw here. */ - dollar_vcol = -1; - - /* Compute the screen column of the start of the completed text. - * Use the cursor to get all wrapping and other settings right. */ - col = curwin->w_cursor.col; - curwin->w_cursor.col = compl_col; - pum_display(compl_match_array, compl_match_arraysize, cur); - curwin->w_cursor.col = col; - } -} - -#define DICT_FIRST (1) /* use just first element in "dict" */ -#define DICT_EXACT (2) /* "dict" is the exact name of a file */ - -/* - * Add any identifiers that match the given pattern in the list of dictionary - * files "dict_start" to the list of completions. - */ - static void -ins_compl_dictionaries( - char_u *dict_start, - char_u *pat, - int flags, /* DICT_FIRST and/or DICT_EXACT */ - int thesaurus) /* Thesaurus completion */ -{ - char_u *dict = dict_start; - char_u *ptr; - char_u *buf; - regmatch_T regmatch; - char_u **files; - int count; - int save_p_scs; - int dir = compl_direction; - - if (*dict == NUL) - { -#ifdef FEAT_SPELL - /* When 'dictionary' is empty and spell checking is enabled use - * "spell". */ - if (!thesaurus && curwin->w_p_spell) - dict = (char_u *)"spell"; - else -#endif - return; - } - - buf = alloc(LSIZE); - if (buf == NULL) - return; - regmatch.regprog = NULL; /* so that we can goto theend */ - - /* If 'infercase' is set, don't use 'smartcase' here */ - save_p_scs = p_scs; - if (curbuf->b_p_inf) - p_scs = FALSE; - - /* When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern - * to only match at the start of a line. Otherwise just match the - * pattern. Also need to double backslashes. */ - if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) - { - char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); - size_t len; - - if (pat_esc == NULL) - goto theend; - len = STRLEN(pat_esc) + 10; - ptr = alloc((unsigned)len); - if (ptr == NULL) - { - vim_free(pat_esc); - goto theend; - } - vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); - regmatch.regprog = vim_regcomp(ptr, RE_MAGIC); - vim_free(pat_esc); - vim_free(ptr); - } - else - { - regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); - if (regmatch.regprog == NULL) - goto theend; - } - - /* ignore case depends on 'ignorecase', 'smartcase' and "pat" */ - regmatch.rm_ic = ignorecase(pat); - while (*dict != NUL && !got_int && !compl_interrupted) - { - /* copy one dictionary file name into buf */ - if (flags == DICT_EXACT) - { - count = 1; - files = &dict; - } - else - { - /* Expand wildcards in the dictionary name, but do not allow - * backticks (for security, the 'dict' option may have been set in - * a modeline). */ - copy_option_part(&dict, buf, LSIZE, ","); -# ifdef FEAT_SPELL - if (!thesaurus && STRCMP(buf, "spell") == 0) - count = -1; - else -# endif - if (vim_strchr(buf, '`') != NULL - || expand_wildcards(1, &buf, &count, &files, - EW_FILE|EW_SILENT) != OK) - count = 0; - } - -# ifdef FEAT_SPELL - if (count == -1) - { - /* Complete from active spelling. Skip "\<" in the pattern, we - * don't use it as a RE. */ - if (pat[0] == '\\' && pat[1] == '<') - ptr = pat + 2; - else - ptr = pat; - spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); - } - else -# endif - if (count > 0) /* avoid warning for using "files" uninit */ - { - ins_compl_files(count, files, thesaurus, flags, - ®match, buf, &dir); - if (flags != DICT_EXACT) - FreeWild(count, files); - } - if (flags != 0) - break; - } - -theend: - p_scs = save_p_scs; - vim_regfree(regmatch.regprog); - vim_free(buf); -} - - static void -ins_compl_files( - int count, - char_u **files, - int thesaurus, - int flags, - regmatch_T *regmatch, - char_u *buf, - int *dir) -{ - char_u *ptr; - int i; - FILE *fp; - int add_r; - - for (i = 0; i < count && !got_int && !compl_interrupted; i++) - { - fp = mch_fopen((char *)files[i], "r"); /* open dictionary file */ - if (flags != DICT_EXACT) - { - vim_snprintf((char *)IObuff, IOSIZE, - _("Scanning dictionary: %s"), (char *)files[i]); - (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); - } - - if (fp != NULL) - { - /* - * Read dictionary file line by line. - * Check each line for a match. - */ - while (!got_int && !compl_interrupted - && !vim_fgets(buf, LSIZE, fp)) - { - ptr = buf; - while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) - { - ptr = regmatch->startp[0]; - if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) - ptr = find_line_end(ptr); - else - ptr = find_word_end(ptr); - add_r = ins_compl_add_infercase(regmatch->startp[0], - (int)(ptr - regmatch->startp[0]), - p_ic, files[i], *dir, 0); - if (thesaurus) - { - char_u *wstart; - - /* - * Add the other matches on the line - */ - ptr = buf; - while (!got_int) - { - /* Find start of the next word. Skip white - * space and punctuation. */ - ptr = find_word_start(ptr); - if (*ptr == NUL || *ptr == NL) - break; - wstart = ptr; - - /* Find end of the word. */ - if (has_mbyte) - /* Japanese words may have characters in - * different classes, only separate words - * with single-byte non-word characters. */ - while (*ptr != NUL) - { - int l = (*mb_ptr2len)(ptr); - - if (l < 2 && !vim_iswordc(*ptr)) - break; - ptr += l; - } - else - ptr = find_word_end(ptr); - - /* Add the word. Skip the regexp match. */ - if (wstart != regmatch->startp[0]) - add_r = ins_compl_add_infercase(wstart, - (int)(ptr - wstart), - p_ic, files[i], *dir, 0); - } - } - if (add_r == OK) - /* if dir was BACKWARD then honor it just once */ - *dir = FORWARD; - else if (add_r == FAIL) - break; - /* avoid expensive call to vim_regexec() when at end - * of line */ - if (*ptr == '\n' || got_int) - break; - } - line_breakcheck(); - ins_compl_check_keys(50, FALSE); - } - fclose(fp); - } - } -} - -/* - * Find the start of the next word. - * Returns a pointer to the first char of the word. Also stops at a NUL. - */ - char_u * -find_word_start(char_u *ptr) -{ - if (has_mbyte) - while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) - ptr += (*mb_ptr2len)(ptr); - else - while (*ptr != NUL && *ptr != '\n' && !vim_iswordc(*ptr)) - ++ptr; - return ptr; -} - -/* - * Find the end of the word. Assumes it starts inside a word. - * Returns a pointer to just after the word. - */ - char_u * -find_word_end(char_u *ptr) -{ - int start_class; - - if (has_mbyte) - { - start_class = mb_get_class(ptr); - if (start_class > 1) - while (*ptr != NUL) - { - ptr += (*mb_ptr2len)(ptr); - if (mb_get_class(ptr) != start_class) - break; - } - } - else - while (vim_iswordc(*ptr)) - ++ptr; - return ptr; -} - -/* - * Find the end of the line, omitting CR and NL at the end. - * Returns a pointer to just after the line. - */ - static char_u * -find_line_end(char_u *ptr) -{ - char_u *s; - - s = ptr + STRLEN(ptr); - while (s > ptr && (s[-1] == CAR || s[-1] == NL)) - --s; - return s; -} - -/* - * Free the list of completions - */ - static void -ins_compl_free(void) -{ - compl_T *match; - int i; - - VIM_CLEAR(compl_pattern); - VIM_CLEAR(compl_leader); - - if (compl_first_match == NULL) - return; - - ins_compl_del_pum(); - pum_clear(); - - compl_curr_match = compl_first_match; - do - { - match = compl_curr_match; - compl_curr_match = compl_curr_match->cp_next; - vim_free(match->cp_str); - /* several entries may use the same fname, free it just once. */ - if (match->cp_flags & FREE_FNAME) - vim_free(match->cp_fname); - for (i = 0; i < CPT_COUNT; ++i) - vim_free(match->cp_text[i]); - vim_free(match); - } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); - compl_first_match = compl_curr_match = NULL; - compl_shown_match = NULL; - compl_old_match = NULL; -} - - static void -ins_compl_clear(void) -{ - compl_cont_status = 0; - compl_started = FALSE; - compl_matches = 0; - VIM_CLEAR(compl_pattern); - VIM_CLEAR(compl_leader); - edit_submode_extra = NULL; - VIM_CLEAR(compl_orig_text); - compl_enter_selects = FALSE; - /* clear v:completed_item */ - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); -} - -/* - * Return TRUE when Insert completion is active. - */ - int -ins_compl_active(void) -{ - return compl_started; -} - - -/* - * Get complete information - */ - void -get_complete_info(list_T *what_list, dict_T *retdict) -{ - int ret = OK; - listitem_T *item; -#define CI_WHAT_MODE 0x01 -#define CI_WHAT_PUM_VISIBLE 0x02 -#define CI_WHAT_ITEMS 0x04 -#define CI_WHAT_SELECTED 0x08 -#define CI_WHAT_INSERTED 0x10 -#define CI_WHAT_ALL 0xff - int what_flag; - - if (what_list == NULL) - what_flag = CI_WHAT_ALL; - else - { - what_flag = 0; - for (item = what_list->lv_first; item != NULL; item = item->li_next) - { - char_u *what = tv_get_string(&item->li_tv); - - if (STRCMP(what, "mode") == 0) - what_flag |= CI_WHAT_MODE; - else if (STRCMP(what, "pum_visible") == 0) - what_flag |= CI_WHAT_PUM_VISIBLE; - else if (STRCMP(what, "items") == 0) - what_flag |= CI_WHAT_ITEMS; - else if (STRCMP(what, "selected") == 0) - what_flag |= CI_WHAT_SELECTED; - else if (STRCMP(what, "inserted") == 0) - what_flag |= CI_WHAT_INSERTED; - } - } - - if (ret == OK && (what_flag & CI_WHAT_MODE)) - ret = dict_add_string(retdict, "mode", ins_compl_mode()); - - if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) - ret = dict_add_number(retdict, "pum_visible", pum_visible()); - - if (ret == OK && (what_flag & CI_WHAT_ITEMS)) - { - list_T *li; - dict_T *di; - compl_T *match; - - li = list_alloc(); - if (li == NULL) - return; - ret = dict_add_list(retdict, "items", li); - if (ret == OK && compl_first_match != NULL) - { - match = compl_first_match; - do - { - if (!(match->cp_flags & ORIGINAL_TEXT)) - { - di = dict_alloc(); - if (di == NULL) - return; - ret = list_append_dict(li, di); - if (ret != OK) - return; - dict_add_string(di, "word", match->cp_str); - dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]); - dict_add_string(di, "menu", match->cp_text[CPT_MENU]); - dict_add_string(di, "kind", match->cp_te