diff --git a/Filelist b/Filelist index 66fe467a9c..579344bddc 100644 --- a/Filelist +++ b/Filelist @@ -68,6 +68,7 @@ SRC_ALL = \ src/fileio.c \ src/filepath.c \ src/findfile.c \ + src/float.c \ src/fold.c \ src/getchar.c \ src/globals.h \ @@ -246,6 +247,7 @@ SRC_ALL = \ src/proto/fileio.pro \ src/proto/filepath.pro \ src/proto/findfile.pro \ + src/proto/float.pro \ src/proto/fold.pro \ src/proto/getchar.pro \ src/proto/gui.pro \ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d7b75b0a7d..0648980eef 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2606,10 +2606,12 @@ getbufline({expr}, {lnum} [, {end}]) getbufvar({expr}, {varname} [, {def}]) any variable {varname} in buffer {expr} getchangelist([{expr}]) List list of change list items -getchar([expr]) Number get one character from the user +getchar([expr]) Number or String + get one character from the user getcharmod() Number modifiers for the last typed character getcharpos({expr}) List position of cursor, mark, etc. getcharsearch() Dict last character search +getcharstr([expr]) String get one character from the user getcmdline() String return the current command-line getcmdpos() Number return cursor position in command-line getcmdtype() String return current command-line type @@ -5244,6 +5246,7 @@ getchar([expr]) *getchar()* Return zero otherwise. If [expr] is 1, only check if a character is available, it is not consumed. Return zero if no character available. + If you prefer always getting a string use |getcharstr()|. Without [expr] and when [expr] is 0 a whole character or special key is returned. If it is a single character, the @@ -5369,6 +5372,20 @@ getcharsearch() *getcharsearch()* :nnoremap , getcharsearch().forward ? ',' : ';' < Also see |setcharsearch()|. + +getcharstr([expr]) *getcharstr()* + Get a single character from the user or input stream as a + string. + If [expr] is omitted, wait until a character is available. + If [expr] is 0 or false, only get a character when one is + available. Return an empty string otherwise. + If [expr] is 1 or true, only check if a character is + available, it is not consumed. Return an empty string + if no character is available. + Otherwise this works like |getchar()|, except that a number + result is converted to a string. + + getcmdline() *getcmdline()* Return the current command-line. Only works when the command line is being edited, thus requires use of |c_CTRL-\_e| or diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index f34891a905..0d66d2bb67 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -952,6 +952,7 @@ Interactive: *interactive-functions* browsedir() put up a directory requester confirm() let the user make a choice getchar() get a character from the user + getcharstr() get a character from the user as a string getcharmod() get modifiers for the last typed character getmousepos() get last known mouse position echoraw() output characters as-is diff --git a/runtime/filetype.vim b/runtime/filetype.vim index b1eb2ecbea..8ca6f6a77b 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1496,7 +1496,7 @@ au BufNewFile,BufRead *.sass setf sass au BufNewFile,BufRead *.sa setf sather " Scala -au BufNewFile,BufRead *.scala setf scala +au BufNewFile,BufRead *.scala,*.sc setf scala " SBT - Scala Build Tool au BufNewFile,BufRead *.sbt setf sbt diff --git a/src/Make_ami.mak b/src/Make_ami.mak index 88b3986508..91246d9bce 100644 --- a/src/Make_ami.mak +++ b/src/Make_ami.mak @@ -117,6 +117,7 @@ SRC += \ fileio.c \ filepath.c \ findfile.c \ + float.c \ fold.c \ getchar.c \ hardcopy.c \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 0be3abbaa1..c33200b33e 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -749,6 +749,7 @@ OBJ = \ $(OUTDIR)/fileio.o \ $(OUTDIR)/filepath.o \ $(OUTDIR)/findfile.o \ + $(OUTDIR)/float.o \ $(OUTDIR)/fold.o \ $(OUTDIR)/getchar.o \ $(OUTDIR)/gui_xim.o \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 30f8175e25..42b6f8ddf6 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -743,6 +743,7 @@ OBJ = \ $(OUTDIR)\fileio.obj \ $(OUTDIR)\filepath.obj \ $(OUTDIR)\findfile.obj \ + $(OUTDIR)\float.obj \ $(OUTDIR)\fold.obj \ $(OUTDIR)\getchar.obj \ $(OUTDIR)\gui_xim.obj \ @@ -1599,6 +1600,8 @@ $(OUTDIR)/filepath.obj: $(OUTDIR) filepath.c $(INCL) $(OUTDIR)/findfile.obj: $(OUTDIR) findfile.c $(INCL) +$(OUTDIR)/float.obj: $(OUTDIR) float.c $(INCL) + $(OUTDIR)/fold.obj: $(OUTDIR) fold.c $(INCL) $(OUTDIR)/getchar.obj: $(OUTDIR) getchar.c $(INCL) @@ -1935,6 +1938,7 @@ proto.h: \ proto/fileio.pro \ proto/filepath.pro \ proto/findfile.pro \ + proto/float.pro \ proto/getchar.pro \ proto/gui_xim.pro \ proto/hardcopy.pro \ diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 3631236360..4928b0be86 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -343,6 +343,7 @@ SRC = \ fileio.c \ filepath.c, \ findfile.c \ + float.c \ fold.c \ getchar.c \ gui_xim.c \ @@ -460,6 +461,7 @@ OBJ = \ fileio.obj \ filepath.obj \ findfile.obj \ + float.obj \ fold.obj \ getchar.obj \ gui_xim.obj \ @@ -860,6 +862,9 @@ findfile.obj : findfile.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 \ errors.h globals.h +float.obj : float.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 errors.h globals.h fold.obj : fold.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 errors.h globals.h diff --git a/src/Makefile b/src/Makefile index e3a7afb420..c0f9e64686 100644 --- a/src/Makefile +++ b/src/Makefile @@ -386,7 +386,8 @@ CClink = $(CC) # Select the architecture supported. Default is to build for the current # platform. Use "both" for a universal binary. That probably doesn't work # when including Perl, Python, etc. -#CONF_OPT_DARWIN = --with-mac-arch=i386 +# NOTE: ppc probably doesn't work anymore, +#CONF_OPT_DARWIN = --with-mac-arch=intel #CONF_OPT_DARWIN = --with-mac-arch=ppc #CONF_OPT_DARWIN = --with-mac-arch=both @@ -436,6 +437,8 @@ CClink = $(CC) # PYTHON # Uncomment lines here when you want to include the Python interface. # This requires at least "normal" features, "tiny" and "small" don't work. +# Python 3 is preferred, Python 2 (often referred to as "Python") has been +# deprecated for a long time. # NOTE: This may cause threading to be enabled, which has side effects (such # as using different libraries and debugging becomes more difficult). # For Python3 support make a symbolic link in /usr/local/bin: @@ -690,16 +693,18 @@ LINT_OPTIONS = -beprxzF # PROFILE_CFLAGS=-g -O0 --coverage -DWE_ARE_PROFILING -DUSE_GCOV_FLUSH -# Uncomment one of the next two lines to compile Vim with the -# address sanitizer (asan) or with the undefined sanitizer. Works with gcc. +# Uncomment the next lines to compile Vim with the address sanitizer (asan) and +# with the undefined sanitizer. Works with gcc. +# You should also use -DEXITFREE to avoid false reports. # May make Vim twice as slow. Errors are reported on stderr. # More at: https://code.google.com/p/address-sanitizer/ # Useful environment variables: # $ export ASAN_OPTIONS="print_stacktrace=1 log_path=asan" # $ export LSAN_OPTIONS="suppressions=`pwd`/testdir/lsan-suppress.txt" # When running tests output can be found in testdir/asan.* -#SANITIZER_CFLAGS = -g -O0 -fsanitize=address -fno-omit-frame-pointer -#SANITIZER_CFLAGS = -g -O0 -fsanitize=undefined -fno-omit-frame-pointer +#SANITIZER_CFLAGS = -g -O0 -fsanitize-recover=all \ +# -fsanitize=address -fsanitize=undefined \ +# -fno-omit-frame-pointer # Similarly when compiling with clang and using ubsan. # $ export UBSAN_OPTIONS="print_stacktrace=1 log_path=ubsan" @@ -979,7 +984,7 @@ SANITIZER_LIBS = $(SANITIZER_CFLAGS) # The value of QUOTESED comes from auto/config.mk. # Uncomment the next line to use the default value. -# QUOTESED = sed -e 's/[\\"]/\\&/g' -e 's/\\"/"/' -e 's/\\";$$/";/' +# QUOTESED = sed -e 's/[\\"]/\\&/g' -e 's/\\"/"/' -e 's/\\";$$/";/' -e 's/ */ /g' ##################### end of system specific lines ################### }}} @@ -1638,6 +1643,7 @@ BASIC_SRC = \ fileio.c \ filepath.c \ findfile.c \ + float.c \ fold.c \ getchar.c \ gui_xim.c \ @@ -1792,6 +1798,7 @@ OBJ_COMMON = \ objects/fileio.o \ objects/filepath.o \ objects/findfile.o \ + objects/float.o \ objects/fold.o \ objects/getchar.o \ objects/gui_xim.o \ @@ -1962,6 +1969,7 @@ PRO_AUTO = \ fileio.pro \ filepath.pro \ findfile.pro \ + float.pro \ fold.pro \ getchar.pro \ gui_xim.pro \ @@ -3270,6 +3278,9 @@ objects/filepath.o: filepath.c objects/findfile.o: findfile.c $(CCC) -o $@ findfile.c +objects/float.o: float.c + $(CCC) -o $@ float.c + objects/fold.o: fold.c $(CCC) -o $@ fold.c @@ -3913,6 +3924,10 @@ objects/findfile.o: findfile.c vim.h protodef.h auto/config.h feature.h os_unix. 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 errors.h globals.h +objects/float.o: float.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 errors.h globals.h objects/fold.o: fold.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/README.md b/src/README.md index b4d9bdd89a..81ca37d0d6 100644 --- a/src/README.md +++ b/src/README.md @@ -56,6 +56,7 @@ locale.c | locale/language handling map.c | mapping and abbreviations mark.c | marks match.c | highlight matching +float.c | floating point functions mbyte.c | multi-byte character handling memfile.c | storing lines for buffers in a swapfile memline.c | storing lines for buffers in memory diff --git a/src/arabic.c b/src/arabic.c index efc6aa66db..f64dab2499 100644 --- a/src/arabic.c +++ b/src/arabic.c @@ -163,8 +163,6 @@ static struct achar { #define a_BYTE_ORDER_MARK 0xfeff -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - /* * Find the struct achar pointer to the given Arabic char. * Returns NULL if not found. @@ -175,7 +173,7 @@ find_achar(int c) int h, m, l; // using binary search to find c - h = ARRAY_SIZE(achars); + h = ARRAY_LENGTH(achars); l = 0; while (l < h) { diff --git a/src/blowfish.c b/src/blowfish.c index 74d8e5416f..342bcc406e 100644 --- a/src/blowfish.c +++ b/src/blowfish.c @@ -23,8 +23,6 @@ #if defined(FEAT_CRYPT) || defined(PROTO) -#define ARRAY_LENGTH(A) (sizeof(A)/sizeof(A[0])) - #define BF_BLOCK 8 #define BF_BLOCK_MASK 7 #define BF_MAX_CFB_LEN (8 * BF_BLOCK) diff --git a/src/channel.c b/src/channel.c index 5438f10011..57c291a4d1 100644 --- a/src/channel.c +++ b/src/channel.c @@ -5078,4 +5078,22 @@ f_ch_status(typval_T *argvars, typval_T *rettv) rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel, part)); } +/* + * Get a string with information about the channel in "varp" in "buf". + * "buf" must be at least NUMBUFLEN long. + */ + char_u * +channel_to_string_buf(typval_T *varp, char_u *buf) +{ + channel_T *channel = varp->vval.v_channel; + char *status = channel_status(channel, -1); + + if (channel == NULL) + vim_snprintf((char *)buf, NUMBUFLEN, "channel %s", status); + else + vim_snprintf((char *)buf, NUMBUFLEN, + "channel %d %s", channel->ch_id, status); + return buf; +} + #endif // FEAT_JOB_CHANNEL diff --git a/src/cindent.c b/src/cindent.c index b2fac1a9fb..ce02402c22 100644 --- a/src/cindent.c +++ b/src/cindent.c @@ -718,7 +718,7 @@ cin_isinit(void) { int i, l; - for (i = 0; i < (int)(sizeof(skip) / sizeof(char *)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(skip); ++i) { l = (int)strlen(skip[i]); if (cin_starts_with(s, skip[i])) diff --git a/src/clipboard.c b/src/clipboard.c index ae98ee1f4a..bcb389b813 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -2094,7 +2094,7 @@ clip_yank_selection( clip_free_selection(cbd); - str_to_reg(y_ptr, type, str, len, 0L, FALSE); + str_to_reg(y_ptr, type, str, len, -1, FALSE); } /* diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 9745aa83ae..9379098dfc 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -2167,7 +2167,7 @@ ExpandFromContext( // Find a context in the table and call the ExpandGeneric() with the // right function to do the expansion. ret = FAIL; - for (i = 0; i < (int)(sizeof(tab) / sizeof(struct expgen)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i) if (xp->xp_context == tab[i].context) { if (tab[i].ic) diff --git a/src/cmdhist.c b/src/cmdhist.c index 1e7ae34034..8bb3cb61c7 100644 --- a/src/cmdhist.c +++ b/src/cmdhist.c @@ -98,7 +98,7 @@ get_history_arg(expand_T *xp UNUSED, int idx) static char_u compl[2] = { NUL, NUL }; char *short_names = ":=@>?/"; int short_names_count = (int)STRLEN(short_names); - int history_name_count = sizeof(history_names) / sizeof(char *) - 1; + int history_name_count = ARRAY_LENGTH(history_names) - 1; if (idx < short_names_count) { diff --git a/src/debugger.c b/src/debugger.c index 6f52e984d1..9c3c4a178c 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -140,7 +140,7 @@ do_debug(char_u *cmd) if (typeahead_saved) { - restore_typeahead(&typeaheadbuf); + restore_typeahead(&typeaheadbuf, TRUE); ignore_script = save_ignore_script; } ex_normal_busy = save_ex_normal_busy; diff --git a/src/dict.c b/src/dict.c index 876eeea492..f0f045b272 100644 --- a/src/dict.c +++ b/src/dict.c @@ -352,8 +352,9 @@ dict_copy(dict_T *orig, int deep, int copyID) dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name) { return (d == get_globvar_dict() - || (SCRIPT_ID_VALID(current_sctx.sc_sid) - && d == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict)) + || (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid) + && d == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict) + || &d->dv_hashtab == get_funccal_local_ht()) && (tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) && var_wrong_func_name(name, TRUE); } diff --git a/src/dosinst.c b/src/dosinst.c index 7f3a0695e7..2d2b95c10a 100644 --- a/src/dosinst.c +++ b/src/dosinst.c @@ -59,7 +59,7 @@ struct choice struct choice choices[30]; // choices the user can make int choice_count = 0; // number of choices available -#define TABLE_SIZE(s) (int)(sizeof(s) / sizeof(*s)) +#define TABLE_SIZE(s) (int)ARRAYSIZE(s) enum { @@ -1527,8 +1527,7 @@ register_openwith( "*\\OpenWithList\\gvim.exe", }; - for (i = 0; ERROR_SUCCESS == lRet - && i < sizeof(openwith) / sizeof(openwith[0]); i++) + for (i = 0; ERROR_SUCCESS == lRet && i < ARRAYSIZE(openwith); i++) lRet = reg_create_key_and_value(hRootKey, openwith[i], NULL, "", flag); } diff --git a/src/errors.h b/src/errors.h index 00d395baea..94a752ae68 100644 --- a/src/errors.h +++ b/src/errors.h @@ -417,3 +417,9 @@ EXTERN char e_failed_to_source_defaults[] INIT(= N_("E1187: Failed to source defaults.vim")); EXTERN char e_cannot_open_terminal_from_command_line_window[] INIT(= N_("E1188: Cannot open a terminal from the command line window")); +EXTERN char e_cannot_use_legacy_with_command_str[] + INIT(= N_("E1189: Cannot use :legacy with this command: %s")); +EXTERN char e_one_argument_too_few[] + INIT(= N_("E1190: One argument too few")); +EXTERN char e_nr_arguments_too_few[] + INIT(= N_("E1190: %d arguments too few")); diff --git a/src/eval.c b/src/eval.c index 7a05d359ba..d0007bbd3e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -104,33 +104,6 @@ num_modulus(varnumber_T n1, varnumber_T n2, int *failed) return (n2 == 0) ? 0 : (n1 % n2); } -#if defined(EBCDIC) || defined(PROTO) -/* - * Compare struct fst by function name. - */ - static int -compare_func_name(const void *s1, const void *s2) -{ - struct fst *p1 = (struct fst *)s1; - struct fst *p2 = (struct fst *)s2; - - return STRCMP(p1->f_name, p2->f_name); -} - -/* - * Sort the function table by function name. - * The sorting of the table above is ASCII dependent. - * On machines using EBCDIC we have to sort it. - */ - static void -sortFunctions(void) -{ - int funcCnt = (int)(sizeof(functions) / sizeof(struct fst)) - 1; - - qsort(functions, (size_t)funcCnt, sizeof(struct fst), compare_func_name); -} -#endif - /* * Initialize the global and v: variables. */ @@ -5086,13 +5059,16 @@ echo_string_core( case VAR_JOB: case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL *tofree = NULL; - r = tv_get_string_buf(tv, numbuf); + r = tv->v_type == VAR_JOB ? job_to_string_buf(tv, numbuf) + : channel_to_string_buf(tv, numbuf); if (composite_val) { *tofree = string_quote(r, FALSE); r = *tofree; } +#endif break; case VAR_INSTR: @@ -5182,43 +5158,6 @@ string_quote(char_u *str, int function) return s; } -#if defined(FEAT_FLOAT) || defined(PROTO) -/* - * Convert the string "text" to a floating point number. - * This uses strtod(). setlocale(LC_NUMERIC, "C") has been used to make sure - * this always uses a decimal point. - * Returns the length of the text that was consumed. - */ - int -string2float( - char_u *text, - float_T *value) // result stored here -{ - char *s = (char *)text; - float_T f; - - // MS-Windows does not deal with "inf" and "nan" properly. - if (STRNICMP(text, "inf", 3) == 0) - { - *value = INFINITY; - return 3; - } - if (STRNICMP(text, "-inf", 3) == 0) - { - *value = -INFINITY; - return 4; - } - if (STRNICMP(text, "nan", 3) == 0) - { - *value = NAN; - return 3; - } - f = strtod(s, &s); - *value = f; - return (int)((char_u *)s - text); -} -#endif - /* * Convert the specified byte index of line 'lnum' in buffer 'buf' to a * character index. Works only for loaded buffers. Returns -1 on failure. diff --git a/src/evalfunc.c b/src/evalfunc.c index 687e8699ff..158e4347fe 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -20,16 +20,7 @@ # include #endif -#ifdef FEAT_FLOAT -static void f_abs(typval_T *argvars, typval_T *rettv); -static void f_acos(typval_T *argvars, typval_T *rettv); -#endif static void f_and(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_asin(typval_T *argvars, typval_T *rettv); -static void f_atan(typval_T *argvars, typval_T *rettv); -static void f_atan2(typval_T *argvars, typval_T *rettv); -#endif #ifdef FEAT_BEVAL static void f_balloon_gettext(typval_T *argvars, typval_T *rettv); static void f_balloon_show(typval_T *argvars, typval_T *rettv); @@ -42,9 +33,6 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp); static void f_byteidx(typval_T *argvars, typval_T *rettv); static void f_byteidxcomp(typval_T *argvars, typval_T *rettv); static void f_call(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_ceil(typval_T *argvars, typval_T *rettv); -#endif static void f_changenr(typval_T *argvars, typval_T *rettv); static void f_char2nr(typval_T *argvars, typval_T *rettv); static void f_charcol(typval_T *argvars, typval_T *rettv); @@ -52,10 +40,6 @@ static void f_charidx(typval_T *argvars, typval_T *rettv); static void f_col(typval_T *argvars, typval_T *rettv); static void f_confirm(typval_T *argvars, typval_T *rettv); static void f_copy(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_cos(typval_T *argvars, typval_T *rettv); -static void f_cosh(typval_T *argvars, typval_T *rettv); -#endif static void f_cursor(typval_T *argsvars, typval_T *rettv); #ifdef MSWIN static void f_debugbreak(typval_T *argvars, typval_T *rettv); @@ -70,17 +54,9 @@ static void f_eval(typval_T *argvars, typval_T *rettv); static void f_eventhandler(typval_T *argvars, typval_T *rettv); static void f_execute(typval_T *argvars, typval_T *rettv); static void f_exists(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_exp(typval_T *argvars, typval_T *rettv); -#endif static void f_expand(typval_T *argvars, typval_T *rettv); static void f_expandcmd(typval_T *argvars, typval_T *rettv); static void f_feedkeys(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_float2nr(typval_T *argvars, typval_T *rettv); -static void f_floor(typval_T *argvars, typval_T *rettv); -static void f_fmod(typval_T *argvars, typval_T *rettv); -#endif static void f_fnameescape(typval_T *argvars, typval_T *rettv); static void f_foreground(typval_T *argvars, typval_T *rettv); static void f_funcref(typval_T *argvars, typval_T *rettv); @@ -118,20 +94,12 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv); static void f_interrupt(typval_T *argvars, typval_T *rettv); static void f_invert(typval_T *argvars, typval_T *rettv); static void f_islocked(typval_T *argvars, typval_T *rettv); -#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) -static void f_isinf(typval_T *argvars, typval_T *rettv); -static void f_isnan(typval_T *argvars, typval_T *rettv); -#endif static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); static void f_len(typval_T *argvars, typval_T *rettv); static void f_libcall(typval_T *argvars, typval_T *rettv); static void f_libcallnr(typval_T *argvars, typval_T *rettv); static void f_line(typval_T *argvars, typval_T *rettv); static void f_line2byte(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_log(typval_T *argvars, typval_T *rettv); -static void f_log10(typval_T *argvars, typval_T *rettv); -#endif #ifdef FEAT_LUA static void f_luaeval(typval_T *argvars, typval_T *rettv); #endif @@ -153,9 +121,6 @@ static void f_or(typval_T *argvars, typval_T *rettv); #ifdef FEAT_PERL static void f_perleval(typval_T *argvars, typval_T *rettv); #endif -#ifdef FEAT_FLOAT -static void f_pow(typval_T *argvars, typval_T *rettv); -#endif static void f_prevnonblank(typval_T *argvars, typval_T *rettv); static void f_printf(typval_T *argvars, typval_T *rettv); static void f_pum_getpos(typval_T *argvars, typval_T *rettv); @@ -176,9 +141,6 @@ static void f_reg_executing(typval_T *argvars, typval_T *rettv); static void f_reg_recording(typval_T *argvars, typval_T *rettv); static void f_rename(typval_T *argvars, typval_T *rettv); static void f_repeat(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_round(typval_T *argvars, typval_T *rettv); -#endif #ifdef FEAT_RUBY static void f_rubyeval(typval_T *argvars, typval_T *rettv); #endif @@ -206,21 +168,11 @@ static void f_sha256(typval_T *argvars, typval_T *rettv); #endif static void f_shellescape(typval_T *argvars, typval_T *rettv); static void f_shiftwidth(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_sin(typval_T *argvars, typval_T *rettv); -static void f_sinh(typval_T *argvars, typval_T *rettv); -#endif static void f_soundfold(typval_T *argvars, typval_T *rettv); static void f_spellbadword(typval_T *argvars, typval_T *rettv); static void f_spellsuggest(typval_T *argvars, typval_T *rettv); static void f_split(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_sqrt(typval_T *argvars, typval_T *rettv); -#endif static void f_srand(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_str2float(typval_T *argvars, typval_T *rettv); -#endif static void f_str2list(typval_T *argvars, typval_T *rettv); static void f_str2nr(typval_T *argvars, typval_T *rettv); static void f_strcharlen(typval_T *argvars, typval_T *rettv); @@ -246,17 +198,10 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv); static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv); static void f_taglist(typval_T *argvars, typval_T *rettv); static void f_tagfiles(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_tan(typval_T *argvars, typval_T *rettv); -static void f_tanh(typval_T *argvars, typval_T *rettv); -#endif static void f_tolower(typval_T *argvars, typval_T *rettv); static void f_toupper(typval_T *argvars, typval_T *rettv); static void f_tr(typval_T *argvars, typval_T *rettv); static void f_trim(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_trunc(typval_T *argvars, typval_T *rettv); -#endif static void f_type(typval_T *argvars, typval_T *rettv); static void f_virtcol(typval_T *argvars, typval_T *rettv); static void f_visualmode(typval_T *argvars, typval_T *rettv); @@ -1000,13 +945,15 @@ static funcentry_T global_functions[] = {"getchangelist", 0, 1, FEARG_1, NULL, ret_list_any, f_getchangelist}, {"getchar", 0, 1, 0, NULL, - ret_number, f_getchar}, + ret_any, f_getchar}, {"getcharmod", 0, 0, 0, NULL, ret_number, f_getcharmod}, {"getcharpos", 1, 1, FEARG_1, NULL, ret_list_number, f_getcharpos}, {"getcharsearch", 0, 0, 0, NULL, ret_dict_any, f_getcharsearch}, + {"getcharstr", 0, 1, 0, NULL, + ret_string, f_getcharstr}, {"getcmdline", 0, 0, 0, NULL, ret_string, f_getcmdline}, {"getcmdpos", 0, 0, 0, NULL, @@ -1855,6 +1802,33 @@ static funcentry_T global_functions[] = ret_number, f_xor}, }; +#if defined(EBCDIC) || defined(PROTO) +/* + * Compare funcentry_T by function name. + */ + static int +compare_func_name(const void *s1, const void *s2) +{ + funcentry_T *p1 = (funcentry_T *)s1; + funcentry_T *p2 = (funcentry_T *)s2; + + return STRCMP(p1->f_name, p2->f_name); +} + +/* + * Sort the function table by function name. + * The sorting of the table above is ASCII dependent. + * On machines using EBCDIC we have to sort it. + */ + void +sortFunctions(void) +{ + size_t funcCnt = ARRAY_LENGTH(global_functions); + + qsort(global_functions, funcCnt, sizeof(funcentry_T), compare_func_name); +} +#endif + /* * Function given to ExpandGeneric() to obtain the list of internal * or user defined function names. @@ -1877,7 +1851,7 @@ get_function_name(expand_T *xp, int idx) return name; } } - if (++intidx < (int)(sizeof(global_functions) / sizeof(funcentry_T))) + if (++intidx < (int)ARRAY_LENGTH(global_functions)) { STRCPY(IObuff, global_functions[intidx].f_name); STRCAT(IObuff, "("); @@ -1923,7 +1897,7 @@ find_internal_func_opt(char_u *name, int implemented) int cmp; int x; - last = (int)(sizeof(global_functions) / sizeof(funcentry_T)) - 1; + last = (int)ARRAY_LENGTH(global_functions) - 1; // Find the function name in the table. Binary search. while (first <= last) @@ -2167,70 +2141,6 @@ non_zero_arg(typval_T *argvars) && *argvars[0].vval.v_string != NUL)); } -#ifdef FEAT_FLOAT -/* - * Get the float value of "argvars[0]" into "f". - * Returns FAIL when the argument is not a Number or Float. - */ - static int -get_float_arg(typval_T *argvars, float_T *f) -{ - if (argvars[0].v_type == VAR_FLOAT) - { - *f = argvars[0].vval.v_float; - return OK; - } - if (argvars[0].v_type == VAR_NUMBER) - { - *f = (float_T)argvars[0].vval.v_number; - return OK; - } - emsg(_("E808: Number or Float required")); - return FAIL; -} - -/* - * "abs(expr)" function - */ - static void -f_abs(typval_T *argvars, typval_T *rettv) -{ - if (argvars[0].v_type == VAR_FLOAT) - { - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = fabs(argvars[0].vval.v_float); - } - else - { - varnumber_T n; - int error = FALSE; - - n = tv_get_number_chk(&argvars[0], &error); - if (error) - rettv->vval.v_number = -1; - else if (n > 0) - rettv->vval.v_number = n; - else - rettv->vval.v_number = -n; - } -} - -/* - * "acos()" function - */ - static void -f_acos(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = acos(f); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "and(expr, expr)" function */ @@ -2241,54 +2151,6 @@ f_and(typval_T *argvars, typval_T *rettv) & tv_get_number_chk(&argvars[1], NULL); } -#ifdef FEAT_FLOAT -/* - * "asin()" function - */ - static void -f_asin(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = asin(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "atan()" function - */ - static void -f_atan(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = atan(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "atan2()" function - */ - static void -f_atan2(typval_T *argvars, typval_T *rettv) -{ - float_T fx = 0.0, fy = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = atan2(fx, fy); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "balloon_show()" function */ @@ -2491,23 +2353,6 @@ f_call(typval_T *argvars, typval_T *rettv) (void)func_call(func, &argvars[1], partial, selfdict, rettv); } -#ifdef FEAT_FLOAT -/* - * "ceil({float})" function - */ - static void -f_ceil(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = ceil(f); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "changenr()" function */ @@ -2745,38 +2590,6 @@ f_copy(typval_T *argvars, typval_T *rettv) item_copy(&argvars[0], rettv, FALSE, 0); } -#ifdef FEAT_FLOAT -/* - * "cos()" function - */ - static void -f_cos(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = cos(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "cosh()" function - */ - static void -f_cosh(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = cosh(f); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * Set the cursor position. * If 'charcol' is TRUE, then use the column number as a character offset. @@ -3189,7 +3002,8 @@ execute_common(typval_T *argvars, typval_T *rettv, int arg_off) if (argvars[arg_off + 1].v_type != VAR_UNKNOWN) { char_u buf[NUMBUFLEN]; - char_u *s = tv_get_string_buf_chk(&argvars[arg_off + 1], buf); + char_u *s = tv_get_string_buf_chk_strict(&argvars[arg_off + 1], buf, + in_vim9script()); if (s == NULL) return; @@ -3324,23 +3138,6 @@ f_exists(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = n; } -#ifdef FEAT_FLOAT -/* - * "exp()" function - */ - static void -f_exp(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = exp(f); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "expand()" function */ @@ -3551,58 +3348,6 @@ f_feedkeys(typval_T *argvars, typval_T *rettv UNUSED) } } -#ifdef FEAT_FLOAT -/* - * "float2nr({float})" function - */ - static void -f_float2nr(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - if (get_float_arg(argvars, &f) == OK) - { - if (f <= (float_T)-VARNUM_MAX + DBL_EPSILON) - rettv->vval.v_number = -VARNUM_MAX; - else if (f >= (float_T)VARNUM_MAX - DBL_EPSILON) - rettv->vval.v_number = VARNUM_MAX; - else - rettv->vval.v_number = (varnumber_T)f; - } -} - -/* - * "floor({float})" function - */ - static void -f_floor(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = floor(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "fmod()" function - */ - static void -f_fmod(typval_T *argvars, typval_T *rettv) -{ - float_T fx = 0.0, fy = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = fmod(fx, fy); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "fnameescape({string})" function */ @@ -6163,7 +5908,7 @@ f_inputrestore(typval_T *argvars UNUSED, typval_T *rettv) { --ga_userinput.ga_len; restore_typeahead((tasave_T *)(ga_userinput.ga_data) - + ga_userinput.ga_len); + + ga_userinput.ga_len, TRUE); // default return is zero == OK } else if (p_verbose > 1) @@ -6270,28 +6015,6 @@ f_islocked(typval_T *argvars, typval_T *rettv) clear_lval(&lv); } -#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) -/* - * "isinf()" function - */ - static void -f_isinf(typval_T *argvars, typval_T *rettv) -{ - if (argvars[0].v_type == VAR_FLOAT && isinf(argvars[0].vval.v_float)) - rettv->vval.v_number = argvars[0].vval.v_float > 0.0 ? 1 : -1; -} - -/* - * "isnan()" function - */ - static void -f_isnan(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT - && isnan(argvars[0].vval.v_float); -} -#endif - /* * "last_buffer_nr()" function. */ @@ -6464,38 +6187,6 @@ f_line2byte(typval_T *argvars UNUSED, typval_T *rettv) #endif } -#ifdef FEAT_FLOAT -/* - * "log()" function - */ - static void -f_log(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = log(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "log10()" function - */ - static void -f_log10(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = log10(f); - else - rettv->vval.v_float = 0.0; -} -#endif - #ifdef FEAT_LUA /* * "luaeval()" function @@ -7016,24 +6707,6 @@ f_perleval(typval_T *argvars, typval_T *rettv) } #endif -#ifdef FEAT_FLOAT -/* - * "pow()" function - */ - static void -f_pow(typval_T *argvars, typval_T *rettv) -{ - float_T fx = 0.0, fy = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = pow(fx, fy); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "prevnonblank()" function */ @@ -7788,33 +7461,6 @@ theend: return retval; } -#ifdef FEAT_FLOAT - -/* - * round() is not in C90, use ceil() or floor() instead. - */ - float_T -vim_round(float_T f) -{ - return f > 0 ? floor(f + 0.5) : ceil(f - 0.5); -} - -/* - * "round({float})" function - */ - static void -f_round(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = vim_round(f); - else - rettv->vval.v_float = 0.0; -} -#endif - #ifdef FEAT_RUBY /* * "rubyeval()" function @@ -8798,38 +8444,6 @@ f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv) rettv->vval.v_number = get_sw_value(curbuf); } -#ifdef FEAT_FLOAT -/* - * "sin()" function - */ - static void -f_sin(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sin(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "sinh()" function - */ - static void -f_sinh(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sinh(f); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "soundfold({word})" function */ @@ -9080,42 +8694,6 @@ theend: p_cpo = save_cpo; } -#ifdef FEAT_FLOAT -/* - * "sqrt()" function - */ - static void -f_sqrt(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sqrt(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -#ifdef FEAT_FLOAT -/* - * "str2float()" function - */ - static void -f_str2float(typval_T *argvars, typval_T *rettv) -{ - char_u *p = skipwhite(tv_get_string(&argvars[0])); - int isneg = (*p == '-'); - - if (*p == '+' || *p == '-') - p = skipwhite(p + 1); - (void)string2float(p, &rettv->vval.v_float); - if (isneg) - rettv->vval.v_float *= -1; - rettv->v_type = VAR_FLOAT; -} -#endif - /* * "str2list()" function */ @@ -9357,7 +8935,7 @@ f_strdisplaywidth(typval_T *argvars, typval_T *rettv) static void f_strwidth(typval_T *argvars, typval_T *rettv) { - char_u *s = tv_get_string(&argvars[0]); + char_u *s = tv_get_string_strict(&argvars[0]); rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1)); } @@ -9957,38 +9535,6 @@ f_taglist(typval_T *argvars, typval_T *rettv) (void)get_tags(rettv->vval.v_list, tag_pattern, fname); } -#ifdef FEAT_FLOAT -/* - * "tan()" function - */ - static void -f_tan(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = tan(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "tanh()" function - */ - static void -f_tanh(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = tanh(f); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "tolower(string)" function */ @@ -10213,24 +9759,6 @@ f_trim(typval_T *argvars, typval_T *rettv) rettv->vval.v_string = vim_strnsave(head, tail - head); } -#ifdef FEAT_FLOAT -/* - * "trunc({float})" function - */ - static void -f_trunc(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - // trunc() is not in C90, use floor() or ceil() instead. - rettv->vval.v_float = f > 0 ? floor(f) : ceil(f); - else - rettv->vval.v_float = 0.0; -} -#endif - /* * "type(expr)" function */ diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h index f09434fe6c..65c82b0565 100644 --- a/src/ex_cmdidxs.h +++ b/src/ex_cmdidxs.h @@ -8,29 +8,29 @@ static const unsigned short cmdidxs1[26] = /* a */ 0, /* b */ 19, /* c */ 43, - /* d */ 109, - /* e */ 134, - /* f */ 158, - /* g */ 175, - /* h */ 181, - /* i */ 190, - /* j */ 209, - /* k */ 211, - /* l */ 216, - /* m */ 279, - /* n */ 299, - /* o */ 319, - /* p */ 331, - /* q */ 370, - /* r */ 373, - /* s */ 393, - /* t */ 462, - /* u */ 507, - /* v */ 518, - /* w */ 539, - /* x */ 553, - /* y */ 563, - /* z */ 564 + /* d */ 110, + /* e */ 135, + /* f */ 162, + /* g */ 179, + /* h */ 185, + /* i */ 194, + /* j */ 213, + /* k */ 215, + /* l */ 220, + /* m */ 283, + /* n */ 303, + /* o */ 323, + /* p */ 335, + /* q */ 374, + /* r */ 377, + /* s */ 397, + /* t */ 466, + /* u */ 512, + /* v */ 523, + /* w */ 544, + /* x */ 558, + /* y */ 568, + /* z */ 569 }; /* @@ -43,9 +43,9 @@ static const unsigned char cmdidxs2[26][26] = { /* a b c d e f g h i j k l m n o p q r s t u v w x y z */ /* a */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0, 0, 7, 15, 0, 16, 0, 0, 0, 0, 0 }, /* b */ { 2, 0, 0, 5, 6, 8, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 23, 0, 0, 0 }, - /* c */ { 3, 12, 16, 18, 20, 22, 25, 0, 0, 0, 0, 33, 37, 40, 46, 56, 58, 59, 60, 0, 62, 0, 65, 0, 0, 0 }, + /* c */ { 3, 12, 16, 18, 20, 22, 25, 0, 0, 0, 0, 33, 38, 41, 47, 57, 59, 60, 61, 0, 63, 0, 66, 0, 0, 0 }, /* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 18, 0, 19, 0, 0, 20, 0, 0, 22, 23, 0, 0, 0, 0, 0, 0, 0 }, - /* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 8, 10, 11, 0, 0, 0, 0, 0, 0, 0, 18, 0, 19, 0, 0 }, + /* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 8, 10, 11, 0, 0, 0, 0, 0, 0, 0, 21, 0, 22, 0, 0 }, /* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0 }, /* g */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 4, 5, 0, 0, 0, 0 }, /* h */ { 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, @@ -60,7 +60,7 @@ static const unsigned char cmdidxs2[26][26] = /* q */ { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* r */ { 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 19, 0, 0, 0, 0 }, /* s */ { 2, 6, 15, 0, 19, 23, 0, 25, 26, 0, 0, 29, 31, 35, 39, 41, 0, 50, 0, 51, 0, 63, 64, 0, 65, 0 }, - /* t */ { 2, 0, 19, 0, 24, 26, 0, 27, 0, 28, 0, 29, 33, 36, 38, 39, 0, 40, 42, 0, 43, 0, 0, 0, 0, 0 }, + /* t */ { 2, 0, 19, 0, 24, 26, 0, 27, 0, 28, 0, 29, 33, 36, 38, 39, 0, 40, 42, 0, 43, 0, 0, 0, 45, 0 }, /* u */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* v */ { 1, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 12, 15, 0, 0, 0, 0, 18, 0, 19, 0, 0, 0, 0, 0 }, /* w */ { 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 8, 0, 9, 10, 0, 0, 0, 12, 13, 0, 0, 0, 0 }, @@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] = /* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; -static const int command_count = 581; +static const int command_count = 586; diff --git a/src/ex_cmds.c b/src/ex_cmds.c index e1497a5a4a..614d41d69f 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -5277,6 +5277,16 @@ ex_drop(exarg_T *eap) */ char_u * skip_vimgrep_pat(char_u *p, char_u **s, int *flags) +{ + return skip_vimgrep_pat_ext(p, s, flags, NULL, NULL); +} + +/* + * As skip_vimgrep_pat() and store the character overwritten by NUL in "cp" + * and the pointer to it in "nulp". + */ + char_u * +skip_vimgrep_pat_ext(char_u *p, char_u **s, int *flags, char_u **nulp, int *cp) { int c; @@ -5287,7 +5297,14 @@ skip_vimgrep_pat(char_u *p, char_u **s, int *flags) *s = p; p = skiptowhite(p); if (s != NULL && *p != NUL) + { + if (nulp != NULL) + { + *nulp = p; + *cp = *p; + } *p++ = NUL; + } } else { @@ -5301,7 +5318,14 @@ skip_vimgrep_pat(char_u *p, char_u **s, int *flags) // Truncate the pattern. if (s != NULL) + { + if (nulp != NULL) + { + *nulp = p; + *cp = *p; + } *p = NUL; + } ++p; // Find the flags diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 57d8f3d261..b84479f411 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -344,6 +344,9 @@ EXCMD(CMD_clist, "clist", qf_list, EXCMD(CMD_clast, "clast", ex_cc, EX_RANGE|EX_COUNT|EX_TRLBAR|EX_BANG, ADDR_UNSIGNED), +EXCMD(CMD_class, "class", ex_ni, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + ADDR_NONE), EXCMD(CMD_close, "close", ex_close, EX_BANG|EX_RANGE|EX_COUNT|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_WINDOWS), @@ -548,9 +551,15 @@ EXCMD(CMD_emenu, "emenu", ex_emenu, EXCMD(CMD_endif, "endif", ex_endif, EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), +EXCMD(CMD_endclass, "endclass", ex_ni, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + ADDR_NONE), EXCMD(CMD_enddef, "enddef", ex_endfunction, EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), +EXCMD(CMD_endenum, "endenum", ex_ni, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + ADDR_NONE), EXCMD(CMD_endfunction, "endfunction", ex_endfunction, EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), @@ -566,6 +575,9 @@ EXCMD(CMD_endwhile, "endwhile", ex_endwhile, EXCMD(CMD_enew, "enew", ex_edit, EX_BANG|EX_TRLBAR, ADDR_NONE), +EXCMD(CMD_enum, "enum", ex_ni, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + ADDR_NONE), EXCMD(CMD_eval, "eval", ex_eval, EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), @@ -1631,6 +1643,9 @@ EXCMD(CMD_tunmenu, "tunmenu", ex_menu, EXCMD(CMD_tunmap, "tunmap", ex_unmap, EX_EXTRA|EX_TRLBAR|EX_NOTRLCOM|EX_CTRLV|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), +EXCMD(CMD_type, "type", ex_ni, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + ADDR_NONE), EXCMD(CMD_undo, "undo", ex_undo, EX_RANGE|EX_COUNT|EX_ZEROR|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_OTHER), diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 1875b0446a..eaf4d0d7d0 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -2887,7 +2887,9 @@ parse_command_modifiers( case 'f': // only accept ":filter {pat} cmd" { - char_u *reg_pat; + char_u *reg_pat; + char_u *nulp = NULL; + int c = 0; if (!checkforcmd_noparen(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) @@ -2908,7 +2910,8 @@ parse_command_modifiers( p = skip_vimgrep_pat(p, NULL, NULL); else // NOTE: This puts a NUL after the pattern. - p = skip_vimgrep_pat(p, ®_pat, NULL); + p = skip_vimgrep_pat_ext(p, ®_pat, NULL, + &nulp, &c); if (p == NULL || *p == NUL) break; if (!skip_only) @@ -2917,6 +2920,9 @@ parse_command_modifiers( vim_regcomp(reg_pat, RE_MAGIC); if (cmod->cmod_filter_regmatch.regprog == NULL) break; + // restore the character overwritten by NUL + if (nulp != NULL) + *nulp = c; } eap->cmd = p; continue; @@ -3741,7 +3747,7 @@ modifier_len(char_u *cmd) if (VIM_ISDIGIT(*cmd)) p = skipwhite(skipdigits(cmd + 1)); - for (i = 0; i < (int)(sizeof(cmdmods) / sizeof(struct cmdmod)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i) { for (j = 0; p[j] != NUL; ++j) if (p[j] != cmdmods[i].name[j]) @@ -3768,7 +3774,7 @@ cmd_exists(char_u *name) char_u *p; // Check command modifiers. - for (i = 0; i < (int)(sizeof(cmdmods) / sizeof(struct cmdmod)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i) { for (j = 0; name[j] != NUL; ++j) if (name[j] != cmdmods[i].name[j]) @@ -8259,7 +8265,7 @@ save_current_state(save_state_T *sst) restore_current_state(save_state_T *sst) { // Restore the previous typeahead. - restore_typeahead(&sst->tabuf); + restore_typeahead(&sst->tabuf, FALSE); msg_scroll = sst->save_msg_scroll; restart_edit = sst->save_restart_edit; @@ -8748,7 +8754,7 @@ find_cmdline_var(char_u *src, int *usedlen) #endif }; - for (i = 0; i < (int)(sizeof(spec_str) / sizeof(char *)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(spec_str); ++i) { len = (int)STRLEN(spec_str[i]); if (STRNCMP(src, spec_str[i], len) == 0) diff --git a/src/fileio.c b/src/fileio.c index efc71350b6..db8d739af8 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -5108,7 +5108,7 @@ vim_tempname( /* * Try the entries in TEMPDIRNAMES to create the temp directory. */ - for (i = 0; i < (int)(sizeof(tempdirs) / sizeof(char *)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(tempdirs); ++i) { # ifndef HAVE_MKDTEMP size_t itmplen; diff --git a/src/filepath.c b/src/filepath.c index b9127c2ae6..e965aafbbd 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -1301,7 +1301,9 @@ f_glob(typval_T *argvars, typval_T *rettv) void f_glob2regpat(typval_T *argvars, typval_T *rettv) { - char_u *pat = tv_get_string_chk(&argvars[0]); + char_u buf[NUMBUFLEN]; + char_u *pat = tv_get_string_buf_chk_strict(&argvars[0], buf, + in_vim9script()); rettv->v_type = VAR_STRING; rettv->vval.v_string = (pat == NULL) diff --git a/src/findfile.c b/src/findfile.c index 4f69c47caa..a72fe45ad9 100644 --- a/src/findfile.c +++ b/src/findfile.c @@ -2848,7 +2848,7 @@ f_simplify(typval_T *argvars, typval_T *rettv) { char_u *p; - p = tv_get_string(&argvars[0]); + p = tv_get_string_strict(&argvars[0]); rettv->vval.v_string = vim_strsave(p); simplify_filename(rettv->vval.v_string); // simplify in place rettv->v_type = VAR_STRING; diff --git a/src/float.c b/src/float.c new file mode 100644 index 0000000000..7c020448aa --- /dev/null +++ b/src/float.c @@ -0,0 +1,477 @@ +/* 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. + */ + +/* + * float.c: Floating point functions + */ +#define USING_FLOAT_STUFF + +#include "vim.h" + +#if (defined(FEAT_EVAL) && defined(FEAT_FLOAT)) || defined(PROTO) + +#ifdef VMS +# include +#endif + +/* + * Convert the string "text" to a floating point number. + * This uses strtod(). setlocale(LC_NUMERIC, "C") has been used to make sure + * this always uses a decimal point. + * Returns the length of the text that was consumed. + */ + int +string2float( + char_u *text, + float_T *value) // result stored here +{ + char *s = (char *)text; + float_T f; + + // MS-Windows does not deal with "inf" and "nan" properly. + if (STRNICMP(text, "inf", 3) == 0) + { + *value = INFINITY; + return 3; + } + if (STRNICMP(text, "-inf", 3) == 0) + { + *value = -INFINITY; + return 4; + } + if (STRNICMP(text, "nan", 3) == 0) + { + *value = NAN; + return 3; + } + f = strtod(s, &s); + *value = f; + return (int)((char_u *)s - text); +} + +/* + * Get the float value of "argvars[0]" into "f". + * Returns FAIL when the argument is not a Number or Float. + */ + static int +get_float_arg(typval_T *argvars, float_T *f) +{ + if (argvars[0].v_type == VAR_FLOAT) + { + *f = argvars[0].vval.v_float; + return OK; + } + if (argvars[0].v_type == VAR_NUMBER) + { + *f = (float_T)argvars[0].vval.v_number; + return OK; + } + emsg(_("E808: Number or Float required")); + return FAIL; +} + +/* + * "abs(expr)" function + */ + void +f_abs(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type == VAR_FLOAT) + { + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = fabs(argvars[0].vval.v_float); + } + else + { + varnumber_T n; + int error = FALSE; + + n = tv_get_number_chk(&argvars[0], &error); + if (error) + rettv->vval.v_number = -1; + else if (n > 0) + rettv->vval.v_number = n; + else + rettv->vval.v_number = -n; + } +} + +/* + * "acos()" function + */ + void +f_acos(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = acos(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "asin()" function + */ + void +f_asin(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = asin(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "atan()" function + */ + void +f_atan(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = atan(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "atan2()" function + */ + void +f_atan2(typval_T *argvars, typval_T *rettv) +{ + float_T fx = 0.0, fy = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &fx) == OK + && get_float_arg(&argvars[1], &fy) == OK) + rettv->vval.v_float = atan2(fx, fy); + else + rettv->vval.v_float = 0.0; +} + +/* + * "ceil({float})" function + */ + void +f_ceil(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = ceil(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "cos()" function + */ + void +f_cos(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = cos(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "cosh()" function + */ + void +f_cosh(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = cosh(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "exp()" function + */ + void +f_exp(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = exp(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "float2nr({float})" function + */ + void +f_float2nr(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + if (get_float_arg(argvars, &f) == OK) + { + if (f <= (float_T)-VARNUM_MAX + DBL_EPSILON) + rettv->vval.v_number = -VARNUM_MAX; + else if (f >= (float_T)VARNUM_MAX - DBL_EPSILON) + rettv->vval.v_number = VARNUM_MAX; + else + rettv->vval.v_number = (varnumber_T)f; + } +} + +/* + * "floor({float})" function + */ + void +f_floor(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = floor(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "fmod()" function + */ + void +f_fmod(typval_T *argvars, typval_T *rettv) +{ + float_T fx = 0.0, fy = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &fx) == OK + && get_float_arg(&argvars[1], &fy) == OK) + rettv->vval.v_float = fmod(fx, fy); + else + rettv->vval.v_float = 0.0; +} + +# if defined(HAVE_MATH_H) || defined(PROTO) +/* + * "isinf()" function + */ + void +f_isinf(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type == VAR_FLOAT && isinf(argvars[0].vval.v_float)) + rettv->vval.v_number = argvars[0].vval.v_float > 0.0 ? 1 : -1; +} + +/* + * "isnan()" function + */ + void +f_isnan(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT + && isnan(argvars[0].vval.v_float); +} +# endif + +/* + * "log()" function + */ + void +f_log(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = log(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "log10()" function + */ + void +f_log10(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = log10(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "pow()" function + */ + void +f_pow(typval_T *argvars, typval_T *rettv) +{ + float_T fx = 0.0, fy = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &fx) == OK + && get_float_arg(&argvars[1], &fy) == OK) + rettv->vval.v_float = pow(fx, fy); + else + rettv->vval.v_float = 0.0; +} + + +/* + * round() is not in C90, use ceil() or floor() instead. + */ + float_T +vim_round(float_T f) +{ + return f > 0 ? floor(f + 0.5) : ceil(f - 0.5); +} + +/* + * "round({float})" function + */ + void +f_round(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = vim_round(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "sin()" function + */ + void +f_sin(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = sin(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "sinh()" function + */ + void +f_sinh(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = sinh(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "sqrt()" function + */ + void +f_sqrt(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = sqrt(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "str2float()" function + */ + void +f_str2float(typval_T *argvars, typval_T *rettv) +{ + char_u *p = skipwhite(tv_get_string_strict(&argvars[0])); + int isneg = (*p == '-'); + + if (*p == '+' || *p == '-') + p = skipwhite(p + 1); + (void)string2float(p, &rettv->vval.v_float); + if (isneg) + rettv->vval.v_float *= -1; + rettv->v_type = VAR_FLOAT; +} + +/* + * "tan()" function + */ + void +f_tan(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = tan(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "tanh()" function + */ + void +f_tanh(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = tanh(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "trunc({float})" function + */ + void +f_trunc(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + // trunc() is not in C90, use floor() or ceil() instead. + rettv->vval.v_float = f > 0 ? floor(f) : ceil(f); + else + rettv->vval.v_float = 0.0; +} + +#endif diff --git a/src/getchar.c b/src/getchar.c index 4ab931ca1e..7209fb560c 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -1414,9 +1414,10 @@ save_typeahead(tasave_T *tp) /* * Restore the typeahead to what it was before calling save_typeahead(). * The allocated memory is freed, can only be called once! + * When "overwrite" is FALSE input typed later is kept. */ void -restore_typeahead(tasave_T *tp) +restore_typeahead(tasave_T *tp, int overwrite UNUSED) { if (tp->typebuf_valid) { @@ -1432,7 +1433,7 @@ restore_typeahead(tasave_T *tp) free_buff(&readbuf2); readbuf2 = tp->save_readbuf2; # ifdef USE_INPUT_BUF - set_input_buf(tp->save_inputbuf); + set_input_buf(tp->save_inputbuf, overwrite); # endif } @@ -2016,10 +2017,10 @@ char_avail(void) #if defined(FEAT_EVAL) || defined(PROTO) /* - * "getchar()" function + * "getchar()" and "getcharstr()" functions */ - void -f_getchar(typval_T *argvars, typval_T *rettv) + static void +getchar_common(typval_T *argvars, typval_T *rettv) { varnumber_T n; int error = FALSE; @@ -2134,6 +2135,42 @@ f_getchar(typval_T *argvars, typval_T *rettv) } } +/* + * "getchar()" function + */ + void +f_getchar(typval_T *argvars, typval_T *rettv) +{ + getchar_common(argvars, rettv); +} + +/* + * "getcharstr()" function + */ + void +f_getcharstr(typval_T *argvars, typval_T *rettv) +{ + getchar_common(argvars, rettv); + + if (rettv->v_type == VAR_NUMBER) + { + char_u temp[7]; // mbyte-char: 6, NUL: 1 + varnumber_T n = rettv->vval.v_number; + int i = 0; + + if (n != 0) + { + if (has_mbyte) + i += (*mb_char2bytes)(n, temp + i); + else + temp[i++] = n; + } + temp[i++] = NUL; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(temp); + } +} + /* * "getcharmod()" function */ diff --git a/src/gui_athena.c b/src/gui_athena.c index 43847404a3..f4aafcd94e 100644 --- a/src/gui_athena.c +++ b/src/gui_athena.c @@ -469,7 +469,7 @@ get_toolbar_pixmap(vimmenu_T *menu, Pixmap *sen) if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL) { if (menu->iconidx >= 0 && menu->iconidx - < (int)(sizeof(built_in_pixmaps) / sizeof(built_in_pixmaps[0]))) + < (int)ARRAY_LENGTH(built_in_pixmaps)) xpm = built_in_pixmaps[menu->iconidx]; else xpm = tb_blank_xpm; diff --git a/src/gui_gtk.c b/src/gui_gtk.c index 77076fcbeb..79a1ac3d8e 100644 --- a/src/gui_gtk.c +++ b/src/gui_gtk.c @@ -1050,6 +1050,9 @@ gui_mch_get_scrollbar_xpadding(void) xpad = gui.formwin->allocation.width - gui.drawarea->allocation.width - gui.scrollbar_width; #endif + if (gui.which_scrollbars[SBAR_LEFT] && gui.which_scrollbars[SBAR_RIGHT]) + xpad -= gui.scrollbar_width; + return (xpad < 0) ? 0 : xpad; } diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 249b20877a..1a3eadad72 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -134,7 +134,7 @@ static const GtkTargetEntry selection_targets[] = {"TEXT", 0, TARGET_TEXT}, {"STRING", 0, TARGET_STRING} }; -#define N_SELECTION_TARGETS (sizeof(selection_targets) / sizeof(selection_targets[0])) +#define N_SELECTION_TARGETS ARRAY_LENGTH(selection_targets) #ifdef FEAT_DND /* @@ -149,7 +149,7 @@ static const GtkTargetEntry dnd_targets[] = {"STRING", 0, TARGET_STRING}, {"text/plain", 0, TARGET_TEXT_PLAIN} }; -# define N_DND_TARGETS (sizeof(dnd_targets) / sizeof(dnd_targets[0])) +# define N_DND_TARGETS ARRAY_LENGTH(dnd_targets) #endif @@ -6853,7 +6853,7 @@ mch_set_mouse_shape(int shape) else id &= ~1; // they are always even (why?) } - else if (shape < (int)(sizeof(mshape_ids) / sizeof(int))) + else if (shape < (int)ARRAY_LENGTH(mshape_ids)) id = mshape_ids[shape]; else return; diff --git a/src/gui_haiku.cc b/src/gui_haiku.cc index a1c7e3eb1b..644c2db665 100644 --- a/src/gui_haiku.cc +++ b/src/gui_haiku.cc @@ -638,7 +638,7 @@ static struct specialkey {0, 0, 0} }; -#define NUM_SPECIAL_KEYS (sizeof(special_keys)/sizeof(special_keys[0])) +#define NUM_SPECIAL_KEYS ARRAY_LENGTH(special_keys) // ---------------- VimApp ---------------- diff --git a/src/gui_photon.c b/src/gui_photon.c index 12b0a3cdee..c89d781056 100644 --- a/src/gui_photon.c +++ b/src/gui_photon.c @@ -34,7 +34,6 @@ # define PhImage_t int #endif -#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a[0])) #define RGB(r, g, b) PgRGB(r, g, b) #define EVENT_BUFFER_SIZE sizeof(PhEvent_t) + 1000 diff --git a/src/gui_w32.c b/src/gui_w32.c index 80a70e25ce..b879aa3c88 100644 --- a/src/gui_w32.c +++ b/src/gui_w32.c @@ -1627,7 +1627,7 @@ gui_mch_get_color(char_u *name) /* * Try to look up a system colour. */ - for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++) + for (i = 0; i < ARRAY_LENGTH(sys_table); i++) if (STRICMP(name, sys_table[i].name) == 0) return GetSysColor(sys_table[i].color); @@ -2942,7 +2942,9 @@ gui_mswin_get_valid_dimensions( int w, int h, int *valid_w, - int *valid_h) + int *valid_h, + int *cols, + int *rows) { int base_width, base_height; @@ -2957,10 +2959,10 @@ gui_mswin_get_valid_dimensions( + gui_mswin_get_menu_height(FALSE) #endif ; - *valid_w = base_width + - ((w - base_width) / gui.char_width) * gui.char_width; - *valid_h = base_height + - ((h - base_height) / gui.char_height) * gui.char_height; + *cols = (w - base_width) / gui.char_width; + *rows = (h - base_height) / gui.char_height; + *valid_w = base_width + *cols * gui.char_width; + *valid_h = base_height + *rows * gui.char_height; } void @@ -4480,6 +4482,46 @@ _OnWindowPosChanged( } #endif + +static HWND hwndTip = NULL; + + static void +show_sizing_tip(int cols, int rows) +{ + TOOLINFOA ti = {sizeof(ti)}; + char buf[32]; + + ti.hwnd = s_hwnd; + ti.uId = (UINT_PTR)s_hwnd; + ti.uFlags = TTF_SUBCLASS | TTF_IDISHWND; + ti.lpszText = buf; + sprintf(buf, "%dx%d", cols, rows); + if (hwndTip == NULL) + { + hwndTip = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, + WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + s_hwnd, NULL, GetModuleHandle(NULL), NULL); + SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti); + SendMessage(hwndTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti); + } + else + { + SendMessage(hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); + } + SendMessage(hwndTip, TTM_POPUP, 0, 0); +} + + static void +destroy_sizing_tip(void) +{ + if (hwndTip != NULL) + { + DestroyWindow(hwndTip); + hwndTip = NULL; + } +} + static int _DuringSizing( UINT fwSide, @@ -4488,10 +4530,11 @@ _DuringSizing( int w, h; int valid_w, valid_h; int w_offset, h_offset; + int cols, rows; w = lprc->right - lprc->left; h = lprc->bottom - lprc->top; - gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h); + gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h, &cols, &rows); w_offset = w - valid_w; h_offset = h - valid_h; @@ -4508,6 +4551,8 @@ _DuringSizing( else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT || fwSide == WMSZ_BOTTOMRIGHT) lprc->bottom -= h_offset; + + show_sizing_tip(cols, rows); return TRUE; } @@ -4648,6 +4693,10 @@ _WndProc( return 0L; #endif + case WM_EXITSIZEMOVE: + destroy_sizing_tip(); + break; + case WM_SIZING: // HANDLE_MSG doesn't seem to handle this one return _DuringSizing((UINT)wParam, (LPRECT)lParam); @@ -5077,7 +5126,7 @@ error: /* * Parse the GUI related command-line arguments. Any arguments used are * deleted from argv, and *argc is decremented accordingly. This is called - * when vim is started, whether or not the GUI has been started. + * when Vim is started, whether or not the GUI has been started. */ void gui_mch_prepare(int *argc, char **argv) diff --git a/src/gui_xmebw.c b/src/gui_xmebw.c index 2c66db121b..3387fbe0b4 100644 --- a/src/gui_xmebw.c +++ b/src/gui_xmebw.c @@ -455,7 +455,7 @@ set_pixmap(XmEnhancedButtonWidget eb) attr.valuemask = XpmColorSymbols | XpmCloseness | XpmColorKey; attr.closeness = 65535; // accuracy isn't crucial attr.colorsymbols = color; - attr.numsymbols = sizeof(color) / sizeof(color[0]); + attr.numsymbols = ARRAY_LENGTH(color); attr.color_key = XPM_MONO; status = XpmCreatePixmapFromData(dpy, root, data, &pix, &mask, &attr); diff --git a/src/hardcopy.c b/src/hardcopy.c index a6df816316..069fa43ddd 100644 --- a/src/hardcopy.c +++ b/src/hardcopy.c @@ -972,8 +972,6 @@ hardcopy_line( * http://www.adobe.com */ -#define NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0])) - #define PRT_PS_DEFAULT_DPI (72) // Default user space resolution #define PRT_PS_DEFAULT_FONTSIZE (10) #define PRT_PS_DEFAULT_BUFFER_SIZE (80) @@ -985,7 +983,7 @@ struct prt_mediasize_S float height; }; -#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S)) +#define PRT_MEDIASIZE_LEN ARRAY_LENGTH(prt_mediasize) static struct prt_mediasize_S prt_mediasize[] = { @@ -1210,33 +1208,33 @@ struct prt_ps_mbfont_S static struct prt_ps_mbfont_S prt_ps_mbfonts[] = { { - NUM_ELEMENTS(j_encodings), + ARRAY_LENGTH(j_encodings), j_encodings, - NUM_ELEMENTS(j_charsets), + ARRAY_LENGTH(j_charsets), j_charsets, "jis_roman", "JIS_X_1983" }, { - NUM_ELEMENTS(sc_encodings), + ARRAY_LENGTH(sc_encodings), sc_encodings, - NUM_ELEMENTS(sc_charsets), + ARRAY_LENGTH(sc_charsets), sc_charsets, "gb_roman", "GB_2312-80" }, { - NUM_ELEMENTS(tc_encodings), + ARRAY_LENGTH(tc_encodings), tc_encodings, - NUM_ELEMENTS(tc_charsets), + ARRAY_LENGTH(tc_charsets), tc_charsets, "cns_roman", "BIG5" }, { - NUM_ELEMENTS(k_encodings), + ARRAY_LENGTH(k_encodings), k_encodings, - NUM_ELEMENTS(k_charsets), + ARRAY_LENGTH(k_charsets), k_charsets, "ks_roman", "KS_X_1992" @@ -1793,12 +1791,12 @@ prt_next_dsc(struct prt_dsc_line_S *p_dsc_line) return FALSE; // Find type of DSC comment - for (comment = 0; comment < (int)NUM_ELEMENTS(prt_dsc_table); comment++) + for (comment = 0; comment < (int)ARRAY_LENGTH(prt_dsc_table); comment++) if (prt_resfile_strncmp(0, prt_dsc_table[comment].string, prt_dsc_table[comment].len) == 0) break; - if (comment != NUM_ELEMENTS(prt_dsc_table)) + if (comment != ARRAY_LENGTH(prt_dsc_table)) { // Return type of comment p_dsc_line->type = prt_dsc_table[comment].type; @@ -2385,7 +2383,7 @@ mch_print_init( int cmap_first = 0; p_mbenc_first = NULL; - for (cmap = 0; cmap < (int)NUM_ELEMENTS(prt_ps_mbfonts); cmap++) + for (cmap = 0; cmap < (int)ARRAY_LENGTH(prt_ps_mbfonts); cmap++) if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap], &p_mbenc)) { diff --git a/src/help.c b/src/help.c index ee6ff18512..28d914c823 100644 --- a/src/help.c +++ b/src/help.c @@ -381,7 +381,7 @@ find_help_tags( // When the string starting with "expr-" and containing '?' and matches // the table, it is taken literally (but ~ is escaped). Otherwise '?' // is recognized as a wildcard. - for (i = (int)(sizeof(expr_table) / sizeof(char *)); --i >= 0; ) + for (i = (int)ARRAY_LENGTH(expr_table); --i >= 0; ) if (STRCMP(arg + 5, expr_table[i]) == 0) { int si = 0, di = 0; diff --git a/src/highlight.c b/src/highlight.c index 946ce4f2b9..9cd2c3e117 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -999,7 +999,7 @@ do_highlight( off = 0; while (arg[off] != NUL) { - for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; ) + for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; ) { len = (int)STRLEN(hl_name_table[i]); if (STRNICMP(arg + off, hl_name_table[i], len) == 0) @@ -1169,7 +1169,7 @@ do_highlight( // reduce calls to STRICMP a bit, it can be slow off = TOUPPER_ASC(*arg); - for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; ) + for (i = ARRAY_LENGTH(color_names); --i >= 0; ) if (off == color_names[i][0] && STRICMP(arg + 1, color_names[i] + 1) == 0) break; diff --git a/src/if_mzsch.c b/src/if_mzsch.c index 58b169231b..0c1c765db5 100644 --- a/src/if_mzsch.c +++ b/src/if_mzsch.c @@ -3799,7 +3799,7 @@ make_modules(void) mod = scheme_primitive_module(vimext_symbol, environment); MZ_GC_CHECK(); // all prims made closed so they can access their own names - for (i = 0; i < (int)(sizeof(prims)/sizeof(prims[0])); i++) + for (i = 0; i < (int)ARRAY_LENGTH(prims); i++) { Vim_Prim *prim = prims + i; closed_prim = scheme_make_closed_prim_w_arity(prim->prim, prim, prim->name, diff --git a/src/job.c b/src/job.c index 8169ba650c..fe5e33cd42 100644 --- a/src/job.c +++ b/src/job.c @@ -1939,4 +1939,34 @@ f_job_stop(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = job_stop(job, argvars, NULL); } +/* + * Get a string with information about the job in "varp" in "buf". + * "buf" must be at least NUMBUFLEN long. + */ + char_u * +job_to_string_buf(typval_T *varp, char_u *buf) +{ + job_T *job = varp->vval.v_job; + char *status; + + if (job == NULL) + return (char_u *)"no process"; + status = job->jv_status == JOB_FAILED ? "fail" + : job->jv_status >= JOB_ENDED ? "dead" + : "run"; +# ifdef UNIX + vim_snprintf((char *)buf, NUMBUFLEN, + "process %ld %s", (long)job->jv_pid, status); +# elif defined(MSWIN) + vim_snprintf((char *)buf, NUMBUFLEN, + "process %ld %s", + (long)job->jv_proc_info.dwProcessId, + status); +# else + // fall-back + vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status); +# endif + return buf; +} + #endif // FEAT_JOB_CHANNEL diff --git a/src/json.c b/src/json.c index db25b70cc4..d0a087a927 100644 --- a/src/json.c +++ b/src/json.c @@ -607,7 +607,7 @@ json_decode_item(js_read_T *reader, typval_T *res, int options) cur_item = res; init_tv(&item); if (res != NULL) - init_tv(res); + init_tv(res); fill_numbuflen(reader); p = reader->js_buf + reader->js_used; @@ -920,6 +920,15 @@ json_decode_item(js_read_T *reader, typval_T *res, int options) if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY && cur_item != NULL) { +#ifdef FEAT_FLOAT + if (cur_item->v_type == VAR_FLOAT) + { + // cannot use a float as a key + emsg(_(e_float_as_string)); + retval = FAIL; + goto theend; + } +#endif top_item->jd_key = tv_get_string_buf_chk(cur_item, key_buf); if (top_item->jd_key == NULL) { diff --git a/src/macros.h b/src/macros.h index c11ee660c9..4ca24c0f5d 100644 --- a/src/macros.h +++ b/src/macros.h @@ -396,3 +396,6 @@ #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif + +// Length of the array. +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) diff --git a/src/main.c b/src/main.c index 9985e03453..3bf660c3da 100644 --- a/src/main.c +++ b/src/main.c @@ -275,8 +275,6 @@ main #ifdef MSWIN { - extern void set_alist_count(void); - // Remember the number of entries in the argument list. If it changes // we don't react on setting 'encoding'. set_alist_count(); @@ -668,7 +666,7 @@ vim_main2(void) #endif /* - * When done something that is not allowed or error message call + * When done something that is not allowed or given an error message call * wait_return. This must be done before starttermcap(), because it may * switch to another screen. It must be done after settmode(TMODE_RAW), * because we want to react on a single key stroke. @@ -1738,7 +1736,7 @@ getout(int exitval) { // give the user a chance to read the (error) message no_wait_return = FALSE; - wait_return(FALSE); +// wait_return(FALSE); } // Position the cursor again, the autocommands may have moved it @@ -3520,7 +3518,7 @@ usage(void) { mch_msg(_(" vim [arguments] ")); mch_msg(_(use[i])); - if (i == (sizeof(use) / sizeof(char_u *)) - 1) + if (i == ARRAY_LENGTH(use) - 1) break; mch_msg(_("\n or:")); } diff --git a/src/map.c b/src/map.c index 6ae0e2a150..e7e57f5d42 100644 --- a/src/map.c +++ b/src/map.c @@ -2481,13 +2481,12 @@ init_mappings(void) if (!gui.starting) # endif { - for (i = 0; - i < (int)(sizeof(cinitmappings) / sizeof(struct initmap)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(cinitmappings); ++i) add_map(cinitmappings[i].arg, cinitmappings[i].mode); } # endif # if defined(FEAT_GUI_MSWIN) || defined(MACOS_X) - for (i = 0; i < (int)(sizeof(initmappings) / sizeof(struct initmap)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(initmappings); ++i) add_map(initmappings[i].arg, initmappings[i].mode); # endif #endif diff --git a/src/mbyte.c b/src/mbyte.c index 697e58520f..4b78eee0e5 100644 --- a/src/mbyte.c +++ b/src/mbyte.c @@ -2850,7 +2850,7 @@ utf_class_buf(int c, buf_T *buf) }; int bot = 0; - int top = sizeof(classes) / sizeof(struct clinterval) - 1; + int top = ARRAY_LENGTH(classes) - 1; int mid; // First quick check for Latin1 characters, use 'iskeyword'. @@ -3948,7 +3948,7 @@ utf_allow_break_before(int cc) }; int first = 0; - int last = sizeof(BOL_prohibition_punct)/sizeof(int) - 1; + int last = ARRAY_LENGTH(BOL_prohibition_punct) - 1; int mid = 0; while (first < last) @@ -3998,7 +3998,7 @@ utf_allow_break_after(int cc) }; int first = 0; - int last = sizeof(EOL_prohibition_punct)/sizeof(int) - 1; + int last = ARRAY_LENGTH(EOL_prohibition_punct) - 1; int mid = 0; while (first < last) @@ -4308,7 +4308,6 @@ mb_charlen(char_u *str) return count; } -#if (defined(FEAT_SPELL) || defined(FEAT_EVAL)) || defined(PROTO) /* * Like mb_charlen() but for a string with specified length. */ @@ -4323,7 +4322,6 @@ mb_charlen_len(char_u *str, int len) return count; } -#endif /* * Try to un-escape a multi-byte character. diff --git a/src/memline.c b/src/memline.c index 5356369c16..a0b642908d 100644 --- a/src/memline.c +++ b/src/memline.c @@ -1312,7 +1312,7 @@ ml_recover(int checkext) } #ifdef FEAT_CRYPT - for (i = 0; i < (int)(sizeof(id1_codes) / sizeof(int)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(id1_codes); ++i) if (id1_codes[i] == b0p->b0_id[1]) b0_cm = i; if (b0_cm > 0) diff --git a/src/menu.c b/src/menu.c index e80e4d1eef..312f10bfa1 100644 --- a/src/menu.c +++ b/src/menu.c @@ -73,7 +73,7 @@ static const char *toolbar_names[] = /* 25 */ "Make", "TagJump", "RunCtags", "WinVSplit", "WinMaxWidth", /* 30 */ "WinMinWidth", "Exit" }; -# define TOOLBAR_NAME_COUNT (sizeof(toolbar_names) / sizeof(char *)) +# define TOOLBAR_NAME_COUNT ARRAY_LENGTH(toolbar_names) #endif /* diff --git a/src/misc2.c b/src/misc2.c index 252ab79a3b..164e78056f 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -1050,7 +1050,6 @@ free_all_mem(void) if (entered_free_all_mem) return; entered_free_all_mem = TRUE; - // Don't want to trigger autocommands from here on. block_autocmds(); @@ -2549,7 +2548,7 @@ static struct key_name_entry // NOTE: When adding a long name update MAX_KEY_NAME_LEN. }; -#define KEY_NAMES_TABLE_LEN (sizeof(key_names_table) / sizeof(struct key_name_entry)) +#define KEY_NAMES_TABLE_LEN ARRAY_LENGTH(key_names_table) /* * Return the modifier mask bit (MOD_MASK_*) which corresponds to the given diff --git a/src/mouse.c b/src/mouse.c index 10667ec7a2..473631f080 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -478,7 +478,7 @@ do_mouse( if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { // double click opens new page - end_visual_mode(); + end_visual_mode_keep_button(); tabpage_new(); tabpage_move(c1 == 0 ? 9999 : c1 - 1); } @@ -490,7 +490,7 @@ do_mouse( // It's like clicking on the status line of a window. if (curwin != old_curwin) - end_visual_mode(); + end_visual_mode_keep_button(); } } else @@ -1588,7 +1588,7 @@ retnomove: #endif if (flags & MOUSE_MAY_STOP_VIS) { - end_visual_mode(); + end_visual_mode_keep_button(); redraw_curbuf_later(INVERTED); // delete the inversion } #if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD) @@ -1737,7 +1737,7 @@ retnomove: #endif && (flags & MOUSE_MAY_STOP_VIS)))) { - end_visual_mode(); + end_visual_mode_keep_button(); redraw_curbuf_later(INVERTED); // delete the inversion } #ifdef FEAT_CMDWIN @@ -1841,7 +1841,7 @@ retnomove: // before moving the cursor for a left click, stop Visual mode if (flags & MOUSE_MAY_STOP_VIS) { - end_visual_mode(); + end_visual_mode_keep_button(); redraw_curbuf_later(INVERTED); // delete the inversion } @@ -2139,6 +2139,14 @@ nv_mouse(cmdarg_T *cap) (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0); } +static int held_button = MOUSE_RELEASE; + + void +reset_held_button() +{ + held_button = MOUSE_RELEASE; +} + /* * Check if typebuf 'tp' contains a terminal mouse code and returns the * modifiers found in typebuf in 'modifiers'. @@ -2164,7 +2172,6 @@ check_termcode_mouse( int is_release, release_is_ambiguous; int wheel_code = 0; int current_button; - static int held_button = MOUSE_RELEASE; static int orig_num_clicks = 1; static int orig_mouse_code = 0x0; # ifdef CHECK_DOUBLE_CLICK diff --git a/src/normal.c b/src/normal.c index aa7363153b..275c6b3b72 100644 --- a/src/normal.c +++ b/src/normal.c @@ -379,7 +379,7 @@ static const struct nv_cmd }; // Number of commands in nv_cmds[]. -#define NV_CMDS_SIZE (sizeof(nv_cmds) / sizeof(struct nv_cmd)) +#define NV_CMDS_SIZE ARRAY_LENGTH(nv_cmds) // Sorted index of commands in nv_cmds[]. static short nv_cmd_idx[NV_CMDS_SIZE]; @@ -1350,11 +1350,18 @@ call_yank_do_autocmd(int regname) /* * End Visual mode. - * This function should ALWAYS be called to end Visual mode, except from - * do_pending_operator(). + * This function or the next should ALWAYS be called to end Visual mode, except + * from do_pending_operator(). */ void -end_visual_mode(void) +end_visual_mode() +{ + end_visual_mode_keep_button(); + reset_held_button(); +} + + void +end_visual_mode_keep_button() { #ifdef FEAT_CLIPBOARD /* @@ -1743,6 +1750,7 @@ clearop(oparg_T *oap) oap->regname = 0; oap->motion_force = NUL; oap->use_reg_one = FALSE; + motion_force = NUL; } void diff --git a/src/ops.c b/src/ops.c index 87fb2a05b4..48d629df27 100644 --- a/src/ops.c +++ b/src/ops.c @@ -82,7 +82,7 @@ get_op_type(int char1, int char2) { if (opchars[i][0] == char1 && opchars[i][1] == char2) break; - if (i == (int)(sizeof(opchars) / sizeof(char [3]) - 1)) + if (i == (int)ARRAY_LENGTH(opchars) - 1) { internal_error("get_op_type()"); break; diff --git a/src/option.c b/src/option.c index 82e61ac043..84f00a83a4 100644 --- a/src/option.c +++ b/src/option.c @@ -148,7 +148,7 @@ set_init_1(int clean_arg) opt_idx = findoption((char_u *)"backupskip"); ga_init2(&ga, 1, 100); - for (n = 0; n < (long)(sizeof(names) / sizeof(char *)); ++n) + for (n = 0; n < (long)ARRAY_LENGTH(names); ++n) { mustfree = FALSE; # ifdef UNIX @@ -6431,8 +6431,7 @@ ExpandSettings( regmatch->rm_ic = ic; if (xp->xp_context != EXPAND_BOOL_SETTINGS) { - for (match = 0; match < (int)(sizeof(names) / sizeof(char *)); - ++match) + for (match = 0; match < (int)ARRAY_LENGTH(names); ++match) if (vim_regexec(regmatch, (char_u *)names[match], (colnr_T)0)) { if (loop == 0) diff --git a/src/optiondefs.h b/src/optiondefs.h index 79c62d6ae0..3d675a69c4 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -3152,7 +3152,7 @@ static struct vimoption options[] = {NULL, NULL, 0, NULL, PV_NONE, {NULL, NULL} SCTX_INIT} }; -#define OPTION_COUNT (sizeof(options) / sizeof(struct vimoption)) +#define OPTION_COUNT ARRAY_LENGTH(options) // The following is needed to make the gen_opt_test.vim script work. // {" diff --git a/src/os_win32.c b/src/os_win32.c index c5bbc2bf2b..874a2c69d8 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -1105,7 +1105,7 @@ decode_key_event( return TRUE; } - for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; ) + for (i = ARRAY_LENGTH(VirtKeyMap); --i >= 0; ) { if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode) { @@ -3045,7 +3045,7 @@ mch_get_user_name( int len) { WCHAR wszUserName[256 + 1]; // UNLEN is 256 - DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR); + DWORD wcch = ARRAY_LENGTH(wszUserName); if (GetUserNameW(wszUserName, &wcch)) { @@ -3072,7 +3072,7 @@ mch_get_host_name( int len) { WCHAR wszHostName[256 + 1]; - DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR); + DWORD wcch = ARRAY_LENGTH(wszHostName); if (GetComputerNameW(wszHostName, &wcch)) { @@ -4757,8 +4757,7 @@ mch_call_shell( WCHAR szShellTitle[512]; // Change the title to reflect that we are in a subshell. - if (GetConsoleTitleW(szShellTitle, - sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0) + if (GetConsoleTitleW(szShellTitle, ARRAY_LENGTH(szShellTitle) - 4) > 0) { if (cmd == NULL) wcscat(szShellTitle, L" :sh"); @@ -4770,7 +4769,7 @@ mch_call_shell( { wcscat(szShellTitle, L" - !"); if ((wcslen(szShellTitle) + wcslen(wn) < - sizeof(szShellTitle)/sizeof(WCHAR))) + ARRAY_LENGTH(szShellTitle))) wcscat(szShellTitle, wn); SetConsoleTitleW(szShellTitle); vim_free(wn); diff --git a/src/popupwin.c b/src/popupwin.c index 35c4b0af5e..cd343f819d 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -402,8 +402,7 @@ get_pos_entry(dict_T *d, int give_error) if (str == NULL) return POPPOS_NONE; - for (nr = 0; nr < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T)); - ++nr) + for (nr = 0; nr < (int)ARRAY_LENGTH(poppos_entries); ++nr) if (STRCMP(str, poppos_entries[nr].pp_name) == 0) return poppos_entries[nr].pp_val; @@ -3042,8 +3041,7 @@ f_popup_getoptions(typval_T *argvars, typval_T *rettv) if (wp->w_close_cb.cb_name != NULL) dict_add_callback(dict, "callback", &wp->w_close_cb); - for (i = 0; i < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T)); - ++i) + for (i = 0; i < (int)ARRAY_LENGTH(poppos_entries); ++i) if (wp->w_popup_pos == poppos_entries[i].pp_val) { dict_add_string(dict, "pos", diff --git a/src/proto.h b/src/proto.h index d58bcea051..48be1bfabb 100644 --- a/src/proto.h +++ b/src/proto.h @@ -90,6 +90,7 @@ extern int _stricoll(char *a, char *b); # include "fileio.pro" # include "filepath.pro" # include "findfile.pro" +# include "float.pro" # include "fold.pro" # include "getchar.pro" # include "gui_xim.pro" diff --git a/src/proto/channel.pro b/src/proto/channel.pro index 6a4fa35fe0..9b1d9d2da4 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -60,4 +60,5 @@ void f_ch_evalraw(typval_T *argvars, typval_T *rettv); void f_ch_sendraw(typval_T *argvars, typval_T *rettv); void f_ch_setoptions(typval_T *argvars, typval_T *rettv); void f_ch_status(typval_T *argvars, typval_T *rettv); +char_u *channel_to_string_buf(typval_T *varp, char_u *buf); /* vim: set ft=c : */ diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 03630c4b03..d31ed1c722 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -56,7 +56,6 @@ int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_ char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int composite_val); char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *string_quote(char_u *str, int function); -int string2float(char_u *text, float_T *value); int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx); int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx); pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum, int charcol); diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro index 2b194486f6..c1ac55f5b9 100644 --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -1,4 +1,5 @@ /* evalfunc.c */ +void sortFunctions(void); char_u *get_function_name(expand_T *xp, int idx); char_u *get_expr_name(expand_T *xp, int idx); int find_internal_func(char_u *name); @@ -23,5 +24,4 @@ void range_list_materialize(list_T *list); float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); void f_string(typval_T *argvars, typval_T *rettv); -void f_fullcommand(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/proto/ex_cmds.pro b/src/proto/ex_cmds.pro index 1711af4537..c083cec5cc 100644 --- a/src/proto/ex_cmds.pro +++ b/src/proto/ex_cmds.pro @@ -39,5 +39,6 @@ int prepare_tagpreview(int undo_sync, int use_previewpopup, use_popup_T use_popu void ex_smile(exarg_T *eap); void ex_drop(exarg_T *eap); char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags); +char_u *skip_vimgrep_pat_ext(char_u *p, char_u **s, int *flags, char_u **nulp, int *cp); void ex_oldfiles(exarg_T *eap); /* vim: set ft=c : */ diff --git a/src/proto/float.pro b/src/proto/float.pro new file mode 100644 index 0000000000..e14ce24735 --- /dev/null +++ b/src/proto/float.pro @@ -0,0 +1,28 @@ +/* math.c */ +int string2float(char_u *text, float_T *value); +void f_abs(typval_T *argvars, typval_T *rettv); +void f_acos(typval_T *argvars, typval_T *rettv); +void f_asin(typval_T *argvars, typval_T *rettv); +void f_atan(typval_T *argvars, typval_T *rettv); +void f_atan2(typval_T *argvars, typval_T *rettv); +void f_ceil(typval_T *argvars, typval_T *rettv); +void f_cos(typval_T *argvars, typval_T *rettv); +void f_cosh(typval_T *argvars, typval_T *rettv); +void f_exp(typval_T *argvars, typval_T *rettv); +void f_float2nr(typval_T *argvars, typval_T *rettv); +void f_floor(typval_T *argvars, typval_T *rettv); +void f_fmod(typval_T *argvars, typval_T *rettv); +void f_isinf(typval_T *argvars, typval_T *rettv); +void f_isnan(typval_T *argvars, typval_T *rettv); +void f_log(typval_T *argvars, typval_T *rettv); +void f_log10(typval_T *argvars, typval_T *rettv); +void f_pow(typval_T *argvars, typval_T *rettv); +void f_round(typval_T *argvars, typval_T *rettv); +void f_sin(typval_T *argvars, typval_T *rettv); +void f_sinh(typval_T *argvars, typval_T *rettv); +void f_sqrt(typval_T *argvars, typval_T *rettv); +void f_str2float(typval_T *argvars, typval_T *rettv); +void f_tan(typval_T *argvars, typval_T *rettv); +void f_tanh(typval_T *argvars, typval_T *rettv); +void f_trunc(typval_T *argvars, typval_T *rettv); +/* vim: set ft=c : */ diff --git a/src/proto/getchar.pro b/src/proto/getchar.pro index cb8b7507e1..5f07ad6c2a 100644 --- a/src/proto/getchar.pro +++ b/src/proto/getchar.pro @@ -32,7 +32,7 @@ int typebuf_maplen(void); void del_typebuf(int len, int offset); int save_typebuf(void); void save_typeahead(tasave_T *tp); -void restore_typeahead(tasave_T *tp); +void restore_typeahead(tasave_T *tp, int overwrite); void openscript(char_u *name, int directly); void close_all_scripts(void); int using_script(void); @@ -46,6 +46,7 @@ int vpeekc_nomap(void); int vpeekc_any(void); int char_avail(void); void f_getchar(typval_T *argvars, typval_T *rettv); +void f_getcharstr(typval_T *argvars, typval_T *rettv); void f_getcharmod(typval_T *argvars, typval_T *rettv); void parse_queued_messages(void); void vungetc(int c); diff --git a/src/proto/job.pro b/src/proto/job.pro index 3627ce399b..5295866a56 100644 --- a/src/proto/job.pro +++ b/src/proto/job.pro @@ -35,4 +35,5 @@ void f_job_setoptions(typval_T *argvars, typval_T *rettv); void f_job_start(typval_T *argvars, typval_T *rettv); void f_job_status(typval_T *argvars, typval_T *rettv); void f_job_stop(typval_T *argvars, typval_T *rettv); +char_u *job_to_string_buf(typval_T *varp, char_u *buf); /* vim: set ft=c : */ diff --git a/src/proto/mouse.pro b/src/proto/mouse.pro index f2a8c3829c..37a2f07cb6 100644 --- a/src/proto/mouse.pro +++ b/src/proto/mouse.pro @@ -13,6 +13,7 @@ int mouse_model_popup(void); int jump_to_mouse(int flags, int *inclusive, int which_button); void nv_mousescroll(cmdarg_T *cap); void nv_mouse(cmdarg_T *cap); +void reset_held_button(void); int check_termcode_mouse(char_u *tp, int *slen, char_u *key_name, char_u *modifiers_start, int idx, int *modifiers); int mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump, int *plines_cache); win_T *mouse_find_win(int *rowp, int *colp, mouse_find_T popup); diff --git a/src/proto/normal.pro b/src/proto/normal.pro index a1e31b341d..30f360b935 100644 --- a/src/proto/normal.pro +++ b/src/proto/normal.pro @@ -3,6 +3,7 @@ void init_normal_cmds(void); void normal_cmd(oparg_T *oap, int toplevel); void check_visual_highlight(void); void end_visual_mode(void); +void end_visual_mode_keep_button(void); void reset_VIsual_and_resel(void); void reset_VIsual(void); void restore_visual_mode(void); diff --git a/src/proto/ui.pro b/src/proto/ui.pro index e75be32b97..f44bad1d65 100644 --- a/src/proto/ui.pro +++ b/src/proto/ui.pro @@ -19,7 +19,7 @@ int vim_is_input_buf_empty(void); int vim_free_in_input_buf(void); int vim_used_in_input_buf(void); char_u *get_input_buf(void); -void set_input_buf(char_u *p); +void set_input_buf(char_u *p, int overwrite); void add_to_input_buf(char_u *s, int len); void add_to_input_buf_csi(char_u *str, int len); void trash_input_buf(void); diff --git a/src/quickfix.c b/src/quickfix.c index 665641c0ff..ad07a5b4ee 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -5971,7 +5971,7 @@ vgr_match_buflines( char_u *str = ml_get_buf(buf, lnum, FALSE); int score; int_u matches[MAX_FUZZY_MATCHES]; - int_u sz = sizeof(matches) / sizeof(matches[0]); + int_u sz = ARRAY_LENGTH(matches); // Fuzzy string match while (fuzzy_match(str + col, spat, FALSE, &score, matches, sz) > 0) diff --git a/src/regexp.c b/src/regexp.c index 8c1431d3c2..805056e3e6 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -202,7 +202,7 @@ get_char_class(char_u **pp) if ((*pp)[1] == ':') { - for (i = 0; i < (int)(sizeof(class_names) / sizeof(*class_names)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(class_names); ++i) if (STRNCMP(*pp + 2, class_names[i], STRLEN(class_names[i])) == 0) { *pp += STRLEN(class_names[i]) + 2; diff --git a/src/register.c b/src/register.c index f4d934393b..5dc8f2896a 100644 --- a/src/register.c +++ b/src/register.c @@ -2894,22 +2894,32 @@ str_to_reg( { for (ss = (char_u **) str; *ss != NULL; ++ss, ++lnum) { - i = (long)STRLEN(*ss); - pp[lnum] = vim_strnsave(*ss, i); - if (i > maxlen) - maxlen = i; + pp[lnum] = vim_strsave(*ss); + if (type == MBLOCK) + { + int charlen = mb_string2cells(*ss, -1); + + if (charlen > maxlen) + maxlen = charlen; + } } } else { for (start = 0; start < len + extraline; start += i + 1) { + int charlen = 0; + for (i = start; i < len; ++i) // find the end of the line + { if (str[i] == '\n') break; + if (type == MBLOCK) + charlen += mb_ptr2cells_len(str + i, len - i); + } i -= start; // i is now length of line - if (i > maxlen) - maxlen = i; + if (charlen > maxlen) + maxlen = charlen; if (append) { --lnum; @@ -2924,7 +2934,7 @@ str_to_reg( mch_memmove(s, y_ptr->y_array[lnum], (size_t)extra); if (append) vim_free(y_ptr->y_array[lnum]); - if (i) + if (i > 0) mch_memmove(s + extra, str + start, (size_t)i); extra += i; s[extra] = NUL; diff --git a/src/screen.c b/src/screen.c index 09837bc2ae..b555d53921 100644 --- a/src/screen.c +++ b/src/screen.c @@ -4858,14 +4858,14 @@ set_chars_option(win_T *wp, char_u **varp) { tab = lcstab; CLEAR_FIELD(lcs_chars); - entries = sizeof(lcstab) / sizeof(struct charstab); + entries = ARRAY_LENGTH(lcstab); if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) varp = &p_lcs; } else { tab = filltab; - entries = sizeof(filltab) / sizeof(struct charstab); + entries = ARRAY_LENGTH(filltab); } // first round: check for valid value, second round: assign values diff --git a/src/scriptfile.c b/src/scriptfile.c index 203cad09fd..bbc415664e 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1788,6 +1788,8 @@ getsourceline( if (line != NULL && options != GETLINE_NONE && vim_strchr(p_cpo, CPO_CONCAT) == NULL) { + int comment_char = in_vim9script() ? '#' : '"'; + // compensate for the one line read-ahead --sp->sourcing_lnum; @@ -1800,7 +1802,8 @@ getsourceline( sp->nextline = get_one_sourceline(sp); if (sp->nextline != NULL && (*(p = skipwhite(sp->nextline)) == '\\' - || (p[0] == '"' && p[1] == '\\' && p[2] == ' ') + || (p[0] == comment_char + && p[1] == '\\' && p[2] == ' ') || (do_vim9_all && (*p == NUL || vim9_comment_start(p))) || (do_bar_cont && p[0] == '|' && p[1] != '|'))) @@ -1842,7 +1845,8 @@ getsourceline( ga_concat(&ga, p); } } - else if (!(p[0] == '"' && p[1] == '\\' && p[2] == ' ') + else if (!(p[0] == (comment_char) + && p[1] == '\\' && p[2] == ' ') && !(do_vim9_all && (*p == NUL || vim9_comment_start(p)))) break; /* drop a # comment or "\ comment line */ diff --git a/src/search.c b/src/search.c index 40c83dfdf9..ce07f756e6 100644 --- a/src/search.c +++ b/src/search.c @@ -4459,7 +4459,7 @@ fuzzy_match_recursive( if (fuzzy_match_recursive(fuzpat, next_char, strIdx + 1, &recursiveScore, strBegin, strLen, matches, recursiveMatches, - sizeof(recursiveMatches)/sizeof(recursiveMatches[0]), + ARRAY_LENGTH(recursiveMatches), nextMatch, recursionCount)) { // Pick best recursive score diff --git a/src/sound.c b/src/sound.c index 9d91f6c086..702fd257a7 100644 --- a/src/sound.c +++ b/src/sound.c @@ -355,10 +355,9 @@ f_sound_playevent(typval_T *argvars, typval_T *rettv) if (wp == NULL) return; - PlaySoundW(wp, NULL, SND_ASYNC | SND_ALIAS); + if (PlaySoundW(wp, NULL, SND_ASYNC | SND_ALIAS)) + rettv->vval.v_number = ++sound_id; free(wp); - - rettv->vval.v_number = ++sound_id; } void diff --git a/src/syntax.c b/src/syntax.c index c3572d8435..60c35bdce8 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -4573,7 +4573,7 @@ get_syn_options( if (strchr(first_letters, *arg) == NULL) break; - for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; ) + for (fidx = ARRAY_LENGTH(flagtab); --fidx >= 0; ) { p = flagtab[fidx].name; for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) diff --git a/src/term.c b/src/term.c index 8069b33738..cb1649c33e 100644 --- a/src/term.c +++ b/src/term.c @@ -6731,7 +6731,7 @@ gui_get_color_cmn(char_u *name) } // Check if the name is one of the colors we know - for (i = 0; i < (int)(sizeof(rgb_table) / sizeof(rgb_table[0])); i++) + for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++) if (STRICMP(name, rgb_table[i].color_name) == 0) return gui_adjust_rgb(rgb_table[i].color); diff --git a/src/terminal.c b/src/terminal.c index 494142850b..2bfaae4bf4 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -5696,7 +5696,7 @@ f_term_getattr(typval_T *argvars, typval_T *rettv) if (attr > HL_ALL) attr = syn_attr2attr(attr); - for (i = 0; i < sizeof(attrs)/sizeof(attrs[0]); ++i) + for (i = 0; i < ARRAY_LENGTH(attrs); ++i) if (STRCMP(name, attrs[i].name) == 0) { rettv->vval.v_number = (attr & attrs[i].attr) != 0 ? 1 : 0; diff --git a/src/termlib.c b/src/termlib.c index e05daae5d4..7c845c5c36 100644 --- a/src/termlib.c +++ b/src/termlib.c @@ -591,7 +591,7 @@ _match(char *s1, char *s2) static char * _find(char *s, char *set) { - for(; *s; s++) + for (; *s; s++) { char *ptr = set; diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim index 4549f48608..0c1e75df59 100644 --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -144,7 +144,7 @@ func Test_string_concatenation() if has('float') let a = 'A' let b = 1.234 - call assert_fails('echo a .. b', 'E806:') + call assert_equal('A1.234', a .. b) endif endfunc diff --git a/src/testdir/test_excmd.vim b/src/testdir/test_excmd.vim index 2e137f0e61..60753a6e82 100644 --- a/src/testdir/test_excmd.vim +++ b/src/testdir/test_excmd.vim @@ -69,6 +69,14 @@ func Test_file_cmd() call assert_fails('3file', 'E474:') call assert_fails('0,0file', 'E474:') call assert_fails('0file abc', 'E474:') + if !has('win32') + " Change the name of the buffer to the same name + new Xfile1 + file Xfile1 + call assert_equal('Xfile1', @%) + call assert_equal('Xfile1', @#) + bw! + endif endfunc " Test for the :drop command diff --git a/src/testdir/test_execute_func.vim b/src/testdir/test_execute_func.vim index 036371715c..c08b2390f2 100644 --- a/src/testdir/test_execute_func.vim +++ b/src/testdir/test_execute_func.vim @@ -2,6 +2,7 @@ source view_util.vim source check.vim +source vim9.vim func NestedEval() let nested = execute('echo "nested\nlines"') @@ -37,8 +38,9 @@ func Test_execute_string() call assert_equal("\nsomething", execute('echo "something"', 'silent!')) call assert_equal("", execute('burp', 'silent!')) if has('float') - call assert_fails('call execute(3.4)', 'E806:') - call assert_fails('call execute("echo \"x\"", 3.4)', 'E806:') + call assert_fails('call execute(3.4)', 'E492:') + call assert_equal("\nx", execute("echo \"x\"", 3.4)) + call CheckDefExecAndScriptFailure(['execute("echo \"x\"", 3.4)'], 'E806:') endif endfunc diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 1b07b0e616..ac227fee17 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -423,7 +423,7 @@ let s:filename_checks = { \ 'sass': ['file.sass'], \ 'sather': ['file.sa'], \ 'sbt': ['file.sbt'], - \ 'scala': ['file.scala'], + \ 'scala': ['file.scala', 'file.sc'], \ 'scheme': ['file.scm', 'file.ss', 'file.rkt'], \ 'scilab': ['file.sci', 'file.sce'], \ 'screen': ['.screenrc', 'screenrc'], diff --git a/src/testdir/test_float_func.vim b/src/testdir/test_float_func.vim index 17b5cdac96..b10bffd728 100644 --- a/src/testdir/test_float_func.vim +++ b/src/testdir/test_float_func.vim @@ -2,6 +2,7 @@ source check.vim CheckFeature float +source vim9.vim func Test_abs() call assert_equal('1.23', string(abs(1.23))) @@ -238,7 +239,9 @@ func Test_str2float() call assert_equal('nan', string(str2float('NaN'))) call assert_equal('nan', string(str2float(' nan '))) - call assert_fails("call str2float(1.2)", 'E806:') + call assert_equal(1.2, str2float(1.2)) + call CheckDefExecFailure(['str2float(1.2)'], 'E1013:') + call CheckScriptFailure(['vim9script', 'str2float(1.2)'], 'E806:') call assert_fails("call str2float([])", 'E730:') call assert_fails("call str2float({})", 'E731:') call assert_fails("call str2float(function('string'))", 'E729:') diff --git a/src/testdir/test_function_lists.vim b/src/testdir/test_function_lists.vim index 7bf8d4d2fc..59180e3294 100644 --- a/src/testdir/test_function_lists.vim +++ b/src/testdir/test_function_lists.vim @@ -96,7 +96,7 @@ func Test_function_lists() sort u w! ++ff=unix Xfunction-list let l:unequal = assert_equalfile("Xsorted_current_global_functions", "Xfunction-list", - \ "\":help functions-list\" incomplete") + \ "\":help function-list\" incomplete") if l:unequal && executable("diff") call system("diff -u Xsorted_current_global_functions Xfunction-list > Xfunction-list.diff") endif diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index f945e9064e..1309554395 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -4,6 +4,7 @@ source shared.vim source check.vim source term_util.vim source screendump.vim +source vim9.vim " Must be done first, since the alternate buffer must be unset. func Test_00_bufexists() @@ -164,11 +165,13 @@ func Test_strwidth() call assert_fails('call strwidth({->0})', 'E729:') call assert_fails('call strwidth([])', 'E730:') call assert_fails('call strwidth({})', 'E731:') - if has('float') - call assert_fails('call strwidth(1.2)', 'E806:') - endif endfor + if has('float') + call assert_equal(3, strwidth(1.2)) + call CheckDefExecAndScriptFailure(['echo strwidth(1.2)'], 'E806:') + endif + set ambiwidth& endfunc @@ -232,7 +235,9 @@ func Test_str2nr() call assert_fails('call str2nr([])', 'E730:') call assert_fails('call str2nr({->2})', 'E729:') if has('float') - call assert_fails('call str2nr(1.2)', 'E806:') + call assert_equal(1, str2nr(1.2)) + call CheckDefExecFailure(['echo str2nr(1.2)'], 'E1013:') + call CheckScriptFailure(['vim9script', 'echo str2nr(1.2)'], 'E806:') endif call assert_fails('call str2nr(10, [])', 'E745:') endfunc @@ -493,7 +498,8 @@ func Test_simplify() call assert_fails('call simplify([])', 'E730:') call assert_fails('call simplify({})', 'E731:') if has('float') - call assert_fails('call simplify(1.2)', 'E806:') + call assert_equal('1.2', simplify(1.2)) + call CheckDefExecAndScriptFailure(['echo simplify(1.2)'], 'E806:') endif endfunc @@ -1723,6 +1729,13 @@ endfunc func Test_getchar() call feedkeys('a', '') call assert_equal(char2nr('a'), getchar()) + call assert_equal(0, getchar(0)) + call assert_equal(0, getchar(1)) + + call feedkeys('a', '') + call assert_equal('a', getcharstr()) + call assert_equal('', getcharstr(0)) + call assert_equal('', getcharstr(1)) call setline(1, 'xxxx') call test_setmouse(1, 3) @@ -2684,8 +2697,13 @@ endfunc func Test_builtin_check() call assert_fails('let g:["trim"] = {x -> " " .. x}', 'E704:') call assert_fails('let g:.trim = {x -> " " .. x}', 'E704:') - call assert_fails('let s:["trim"] = {x -> " " .. x}', 'E704:') - call assert_fails('let s:.trim = {x -> " " .. x}', 'E704:') + call assert_fails('let l:["trim"] = {x -> " " .. x}', 'E704:') + call assert_fails('let l:.trim = {x -> " " .. x}', 'E704:') + let lines =<< trim END + vim9script + var s:trim = (x) => " " .. x + END + call CheckScriptFailure(lines, 'E704:') call assert_fails('call extend(g:, #{foo: { -> "foo" }})', 'E704:') let g:bar = 123 diff --git a/src/testdir/test_glob2regpat.vim b/src/testdir/test_glob2regpat.vim index 2907b286db..ab459bba33 100644 --- a/src/testdir/test_glob2regpat.vim +++ b/src/testdir/test_glob2regpat.vim @@ -1,8 +1,11 @@ " Test glob2regpat() +source vim9.vim + func Test_glob2regpat_invalid() if has('float') - call assert_fails('call glob2regpat(1.33)', 'E806:') + call assert_equal('^1\.33$', glob2regpat(1.33)) + call CheckDefExecAndScriptFailure(['echo glob2regpat(1.33)'], 'E806:') endif call assert_fails('call glob2regpat("}")', 'E219:') call assert_fails('call glob2regpat("{")', 'E220:') diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index 46b030b86e..8351acb410 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -859,7 +859,7 @@ func Test_listdict_extend() call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 'error')", 'E737:') call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 'xxx')", 'E475:') if has('float') - call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 1.2)", 'E806:') + call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 1.2)", 'E475:') endif call assert_equal({'a': 'A', 'b': 'B'}, d) @@ -1022,9 +1022,9 @@ func Test_listdict_index() call assert_fails("let l = insert([1,2,3], 4, [])", 'E745:') let l = [1, 2, 3] call assert_fails("let l[i] = 3", 'E121:') - call assert_fails("let l[1.1] = 4", 'E806:') + call assert_fails("let l[1.1] = 4", 'E805:') call assert_fails("let l[:i] = [4, 5]", 'E121:') - call assert_fails("let l[:3.2] = [4, 5]", 'E806:') + call assert_fails("let l[:3.2] = [4, 5]", 'E805:') let t = test_unknown() call assert_fails("echo t[0]", 'E685:') endfunc diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim index 88aeafda7e..1d0a77c17c 100644 --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -1147,7 +1147,9 @@ endfunc " Test for the popup menu with the 'rightleft' option set func Test_pum_rightleft() + CheckFeature rightleft CheckScreendump + let lines =<< trim END abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz vim @@ -1204,11 +1206,13 @@ func Test_pum_scrollbar() call term_sendkeys(buf, "\\dd") call term_wait(buf) - call term_sendkeys(buf, ":set rightleft\") - call term_wait(buf) - call term_sendkeys(buf, "Go\\\") - call term_wait(buf) - call VerifyScreenDump(buf, 'Test_pum_scrollbar_02', {'rows': 7}) + if has('rightleft') + call term_sendkeys(buf, ":set rightleft\") + call term_wait(buf) + call term_sendkeys(buf, "Go\\\") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_pum_scrollbar_02', {'rows': 7}) + endif call StopVimInTerminal(buf) call delete('Xtest1') diff --git a/src/testdir/test_recover.vim b/src/testdir/test_recover.vim index a1ff7d92af..63225f31d9 100644 --- a/src/testdir/test_recover.vim +++ b/src/testdir/test_recover.vim @@ -133,4 +133,261 @@ func Test_nocatch_process_still_running() call delete(swname) endfunc +" Test for :recover with multiple swap files +func Test_recover_multiple_swap_files() + CheckUnix + new Xfile1 + call setline(1, ['a', 'b', 'c']) + preserve + let b = readblob(swapname('')) + call writefile(b, '.Xfile1.swm') + call writefile(b, '.Xfile1.swn') + call writefile(b, '.Xfile1.swo') + %bw! + call feedkeys(":recover Xfile1\3\q", 'xt') + call assert_equal(['a', 'b', 'c'], getline(1, '$')) + " try using out-of-range number to select a swap file + bw! + call feedkeys(":recover Xfile1\4\q", 'xt') + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + call feedkeys(":recover Xfile1\0\q", 'xt') + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + + call delete('.Xfile1.swm') + call delete('.Xfile1.swn') + call delete('.Xfile1.swo') +endfunc + +" Test for :recover using an empty swap file +func Test_recover_empty_swap_file() + CheckUnix + call writefile([], '.Xfile1.swp') + let msg = execute('recover Xfile1') + call assert_match('Unable to read block 0 from .Xfile1.swp', msg) + call assert_equal('Xfile1', @%) + bw! + " :recover from an empty buffer + call assert_fails('recover', 'E305:') + call delete('.Xfile1.swp') +endfunc + +" Test for :recover using a corrupted swap file +" Refer to the comments in the memline.c file for the swap file headers +" definition. +func Test_recover_corrupted_swap_file() + CheckUnix + + " recover using a partial swap file + call writefile(0z1234, '.Xfile1.swp') + call assert_fails('recover Xfile1', 'E295:') + bw! + + " recover using invalid content in the swap file + call writefile([repeat('1', 2*1024)], '.Xfile1.swp') + call assert_fails('recover Xfile1', 'E307:') + call delete('.Xfile1.swp') + + " :recover using a swap file with a corrupted header + edit Xfile1 + preserve + let sn = swapname('') + let b = readblob(sn) + let save_b = copy(b) + bw! + " Run these tests only on little-endian systems. These tests fail on a + " big-endian system (IBM S390x system). + if b[1008:1011] == 0z33323130 + \ && b[4096:4097] == 0z7470 + \ && b[8192:8193] == 0z6164 + + " clear the B0_MAGIC_LONG field + let b[1008:1011] = 0z00000000 + call writefile(b, sn) + let msg = execute('recover Xfile1') + call assert_match('the file has been damaged', msg) + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + + " reduce the page size + let b = copy(save_b) + let b[12:15] = 0z00010000 + call writefile(b, sn) + let msg = execute('recover Xfile1') + call assert_match('page size is smaller than minimum value', msg) + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + + " clear the pointer ID + let b = copy(save_b) + let b[4096:4097] = 0z0000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E310:') + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + + " set the number of pointers in a pointer block to zero + let b = copy(save_b) + let b[4098:4099] = 0z0000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???EMPTY BLOCK'], getline(1, '$')) + bw! + + " set the block number in a pointer entry to a negative number + let b = copy(save_b) + let b[4104:4111] = 0z00000000.00000080 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???LINES MISSING'], getline(1, '$')) + bw! + + " clear the data block ID + let b = copy(save_b) + let b[8192:8193] = 0z0000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???BLOCK MISSING'], getline(1, '$')) + bw! + + " set the number of lines in the data block to zero + let b = copy(save_b) + let b[8208:8211] = 0z00000000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['??? from here until ???END lines may have been inserted/deleted', + \ '???END'], getline(1, '$')) + bw! + + " use an invalid text start for the lines in a data block + let b = copy(save_b) + let b[8216:8219] = 0z00000000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???'], getline(1, '$')) + bw! + + " use an incorrect text end (db_txt_end) for the data block + let b = copy(save_b) + let b[8204:8207] = 0z80000000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['??? from here until ???END lines may be messed up', '', + \ '???END'], getline(1, '$')) + bw! + + " remove the data block + let b = copy(save_b) + call writefile(b[:8191], sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???MANY LINES MISSING'], getline(1, '$')) + endif + + bw! + call delete(sn) +endfunc + +" Test for :recover using an encrypted swap file +func Test_recover_encrypted_swap_file() + CheckUnix + + " Recover an encrypted file from the swap file without the original file + new Xfile1 + call feedkeys(":X\vim\vim\", 'xt') + call setline(1, ['aaa', 'bbb', 'ccc']) + preserve + let b = readblob('.Xfile1.swp') + call writefile(b, '.Xfile1.swm') + bw! + call feedkeys(":recover Xfile1\vim\\", 'xt') + call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) + bw! + call delete('.Xfile1.swm') + + " Recover an encrypted file from the swap file with the original file + new Xfile1 + call feedkeys(":X\vim\vim\", 'xt') + call setline(1, ['aaa', 'bbb', 'ccc']) + update + call setline(1, ['111', '222', '333']) + preserve + let b = readblob('.Xfile1.swp') + call writefile(b, '.Xfile1.swm') + bw! + call feedkeys(":recover Xfile1\vim\\", 'xt') + call assert_equal(['111', '222', '333'], getline(1, '$')) + call assert_true(&modified) + bw! + call delete('.Xfile1.swm') + call delete('Xfile1') +endfunc + +" Test for :recover using a unreadable swap file +func Test_recover_unreadble_swap_file() + CheckUnix + CheckNotRoot + new Xfile1 + let b = readblob('.Xfile1.swp') + call writefile(b, '.Xfile1.swm') + bw! + call setfperm('.Xfile1.swm', '-w-------') + call assert_fails('recover Xfile1', 'E306:') + call delete('.Xfile1.swm') +endfunc + +" Test for using :recover when the original file and the swap file have the +" same contents. +func Test_recover_unmodified_file() + CheckUnix + call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1') + edit Xfile1 + preserve + let b = readblob('.Xfile1.swp') + %bw! + call writefile(b, '.Xfile1.swz') + let msg = execute('recover Xfile1') + call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) + call assert_false(&modified) + call assert_match('Buffer contents equals file contents', msg) + bw! + call delete('Xfile1') + call delete('.Xfile1.swz') +endfunc + +" Test for recovering a file when editing a symbolically linked file +func Test_recover_symbolic_link() + CheckUnix + call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1') + silent !ln -s Xfile1 Xfile2 + edit Xfile2 + call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t')) + preserve + let b = readblob('.Xfile1.swp') + %bw! + call writefile([], 'Xfile1') + call writefile(b, '.Xfile1.swp') + silent! recover Xfile2 + call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) + call assert_true(&modified) + update + %bw! + call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1')) + call delete('Xfile1') + call delete('Xfile2') + call delete('.Xfile1.swp') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_sound.vim b/src/testdir/test_sound.vim index c8f6305efb..ff58262bff 100644 --- a/src/testdir/test_sound.vim +++ b/src/testdir/test_sound.vim @@ -1,12 +1,12 @@ " Tests for the sound feature +source check.vim source shared.vim -if !has('sound') - throw 'Skipped: sound feature not available' -endif +CheckFeature sound func PlayCallback(id, result) + let g:playcallback_count += 1 let g:id = a:id let g:result = a:result endfunc @@ -15,20 +15,24 @@ func Test_play_event() if has('win32') throw 'Skipped: Playing event with callback is not supported on Windows' endif + let g:playcallback_count = 0 let g:id = 0 let id = 'bell'->sound_playevent('PlayCallback') if id == 0 throw 'Skipped: bell event not available' endif + " Stop it quickly, avoid annoying the user. sleep 20m eval id->sound_stop() call WaitForAssert({-> assert_equal(id, g:id)}) call assert_equal(1, g:result) " sound was aborted + call assert_equal(1, g:playcallback_count) endfunc func Test_play_silent() let fname = fnamemodify('silent.wav', '%p') + let g:playcallback_count = 0 " play without callback let id1 = sound_playfile(fname) @@ -41,6 +45,7 @@ func Test_play_silent() call assert_true(id2 > 0) call WaitForAssert({-> assert_equal(id2, g:id)}) call assert_equal(0, g:result) + call assert_equal(1, g:playcallback_count) let id2 = sound_playfile(fname, 'PlayCallback') call assert_true(id2 > 0) @@ -48,6 +53,13 @@ func Test_play_silent() call sound_clear() call WaitForAssert({-> assert_equal(id2, g:id)}) call assert_equal(1, g:result) " sound was aborted + call assert_equal(2, g:playcallback_count) + + " Play 2 sounds almost at the same time to exercise + " code with multiple callbacks in the callback list. + call sound_playfile(fname, 'PlayCallback') + call sound_playfile(fname, 'PlayCallback') + call WaitForAssert({-> assert_equal(4, g:playcallback_count)}) " recursive use was causing a crash func PlayAgain(id, fname) @@ -62,4 +74,23 @@ func Test_play_silent() call WaitForAssert({-> assert_true(g:id > 0)}) endfunc +func Test_play_event_error() + " FIXME: sound_playevent() doesn't return 0 in case of error on Windows. + if !has('win32') + call assert_equal(0, sound_playevent('')) + call assert_equal(0, sound_playevent(test_null_string())) + call assert_equal(0, sound_playevent('doesnotexist')) + call assert_equal(0, sound_playevent('doesnotexist', 'doesnotexist')) + call assert_equal(0, sound_playevent(test_null_string(), test_null_string())) + call assert_equal(0, sound_playevent(test_null_string(), test_null_function())) + endif + + call assert_equal(0, sound_playfile('')) + call assert_equal(0, sound_playfile(test_null_string())) + call assert_equal(0, sound_playfile('doesnotexist')) + call assert_equal(0, sound_playfile('doesnotexist', 'doesnotexist')) + call assert_equal(0, sound_playfile(test_null_string(), test_null_string())) + call assert_equal(0, sound_playfile(test_null_string(), test_null_function())) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index 0d77101d4a..dfd848d004 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -277,18 +277,20 @@ func Test_V_arg() endfunc " Test that an error is shown when the defaults.vim file could not be read -" TODO: disabled - this causes ASAN errors for unknown reasons -"func Test_defaults_error() -" " Can't catch the output of gvim. -" CheckNotGui -" CheckNotMSWindows -" -" let out = system('VIMRUNTIME=/tmp ' .. GetVimCommand() .. ' --clean -cq') -" call assert_match("E1187: Failed to source defaults.vim", out) -" -" let out = system('VIMRUNTIME=/tmp ' .. GetVimCommand() .. ' -u DEFAULTS -cq') -" call assert_match("E1187: Failed to source defaults.vim", out) -"endfunc +func Test_defaults_error() + " Can't catch the output of gvim. + CheckNotGui + CheckNotMSWindows + " For unknown reasons freeing all memory does not work here, even though + " EXITFREE is defined. + CheckNotAsan + + let out = system('VIMRUNTIME=/tmp ' .. GetVimCommand() .. ' --clean -cq') + call assert_match("E1187: Failed to source defaults.vim", out) + + let out = system('VIMRUNTIME=/tmp ' .. GetVimCommand() .. ' -u DEFAULTS -cq') + call assert_match("E1187: Failed to source defaults.vim", out) +endfunc " Test the '-q [errorfile]' argument. func Test_q_arg() diff --git a/src/testdir/test_substitute.vim b/src/testdir/test_substitute.vim index 4c99f81870..0c367485b8 100644 --- a/src/testdir/test_substitute.vim +++ b/src/testdir/test_substitute.vim @@ -1,6 +1,7 @@ " Tests for multi-line regexps with ":s". source shared.vim +source check.vim func Test_multiline_subst() enew! @@ -453,6 +454,13 @@ func Test_substitute_partial() call assert_fails("call substitute('123', '2', Replacer, 'g')", 'E118:') endfunc +func Test_substitute_float() + CheckFeature float + + call assert_equal('number 1.23', substitute('number ', '$', { -> 1.23 }, '')) + vim9 assert_equal('number 1.23', substitute('number ', '$', () => 1.23, '')) +endfunc + " Tests for *sub-replace-special* and *sub-replace-expression* on :substitute. " Execute a list of :substitute command tests diff --git a/src/testdir/test_swap.vim b/src/testdir/test_swap.vim index 5f3f24da6a..90afb4d6f0 100644 --- a/src/testdir/test_swap.vim +++ b/src/testdir/test_swap.vim @@ -483,4 +483,79 @@ func Test_swap_auto_delete() augroup! test_swap_recover_ext endfunc +" Test for renaming a buffer when the swap file is deleted out-of-band +func Test_missing_swap_file() + CheckUnix + new Xfile1 + call delete(swapname('')) + call assert_fails('file Xfile2', 'E301:') + call assert_equal('Xfile2', bufname()) + call assert_true(bufexists('Xfile1')) + call assert_true(bufexists('Xfile2')) + %bw! +endfunc + +" Test for :preserve command +func Test_preserve() + new Xfile1 + setlocal noswapfile + call assert_fails('preserve', 'E313:') + bw! +endfunc + +" Test for the v:swapchoice variable +func Test_swapchoice() + call writefile(['aaa', 'bbb'], 'Xfile1') + edit Xfile1 + preserve + let swapfname = swapname('') + let b = readblob(swapfname) + bw! + call writefile(b, swapfname) + + autocmd! SwapExists + + " Test for v:swapchoice = 'o' (readonly) + augroup test_swapchoice + autocmd! + autocmd SwapExists * let v:swapchoice = 'o' + augroup END + edit Xfile1 + call assert_true(&readonly) + call assert_equal(['aaa', 'bbb'], getline(1, '$')) + %bw! + call assert_true(filereadable(swapfname)) + + " Test for v:swapchoice = 'a' (abort) + augroup test_swapchoice + autocmd! + autocmd SwapExists * let v:swapchoice = 'a' + augroup END + try + edit Xfile1 + catch /^Vim:Interrupt$/ + endtry + call assert_equal('', @%) + call assert_true(bufexists('Xfile1')) + %bw! + call assert_true(filereadable(swapfname)) + + " Test for v:swapchoice = 'd' (delete) + augroup test_swapchoice + autocmd! + autocmd SwapExists * let v:swapchoice = 'd' + augroup END + edit Xfile1 + call assert_equal('Xfile1', @%) + %bw! + call assert_false(filereadable(swapfname)) + + call delete('Xfile1') + call delete(swapfname) + augroup test_swapchoice + autocmd! + augroup END + augroup! test_swapchoice +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 3fa5eb3065..b98c840ce0 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -810,6 +810,15 @@ def Test_map_function_arg() assert_equal(['0:a', '1:b', '2:c'], l) END CheckDefAndScriptSuccess(lines) + + lines =<< trim END + range(3)->map((a, b, c) => a + b + c) + END + CheckDefExecAndScriptFailure(lines, 'E1190: One argument too few') + lines =<< trim END + range(3)->map((a, b, c, d) => a + b + c + d) + END + CheckDefExecAndScriptFailure(lines, 'E1190: 2 arguments too few') enddef def Test_map_item_type() @@ -1095,7 +1104,7 @@ def Test_set_get_bufline() assert_equal([], getbufline(b, 2, 1)) if has('job') - setbufline(b, 2, [function('eval'), {key: 123}, test_null_job()]) + setbufline(b, 2, [function('eval'), {key: 123}, string(test_null_job())]) assert_equal(["function('eval')", "{'key': 123}", "no process"], @@ -1241,6 +1250,16 @@ def Test_submatch() actual->assert_equal(expected) enddef +def Test_substitute() + var res = substitute('A1234', '\d', 'X', '') + assert_equal('AX234', res) + + if has('job') + assert_fails('"text"->substitute(".*", () => job_start(":"), "")', 'E908: using an invalid value as a String: job') + assert_fails('"text"->substitute(".*", () => job_start(":")->job_getchannel(), "")', 'E908: using an invalid value as a String: channel') + endif +enddef + def Test_synID() new setline(1, "text") diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 87832ce2eb..23c5cfacf6 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -34,6 +34,10 @@ def Test_edit_wildcards() CheckDefFailure(['edit `=xxx`'], 'E1001:') CheckDefFailure(['edit `="foo"'], 'E1083:') + + var files = ['file 1', 'file%2', 'file# 3'] + args `=files` + assert_equal(files, argv()) enddef def Test_expand_alternate_file() @@ -530,6 +534,14 @@ def Test_command_modifier_filter() assert_equal(execute('filter /piyo/ registers abc'), expected) END CheckDefAndScriptSuccess(lines) + + # also do this compiled + lines =<< trim END + @a = 'very specific z3d37dh234 string' + filter z3d37dh234 registers + assert_match('very specific z3d37dh234 string', Screenline(&lines)) + END + CheckDefAndScriptSuccess(lines) enddef def Test_win_command_modifiers() @@ -1232,6 +1244,13 @@ def Test_substitute_expr() END CheckScriptSuccess(lines) unlet g:cond + + # List results in multiple lines + new + setline(1, 'some text here') + s/text/\=['aaa', 'bbb', 'ccc']/ + assert_equal(['some aaa', 'bbb', 'ccc', ' here'], getline(1, '$')) + bwipe! enddef def Test_redir_to_var() diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index c4482a5d9c..965399bca1 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -121,6 +121,23 @@ def Test_disassemble_exec_expr() res) enddef +if has('python3') + def s:PyHeredoc() + python3 << EOF + print('hello') +EOF + enddef + + def Test_disassemble_python_heredoc() + var res = execute('disass s:PyHeredoc') + assert_match('\d*_PyHeredoc.*' .. + " python3 << EOF^@ print('hello')^@EOF\\_s*" .. + '\d EXEC_SPLIT python3 << EOF^@ print(''hello'')^@EOF\_s*' .. + '\d RETURN 0', + res) + enddef +endif + def s:Substitute() var expr = "abc" :%s/a/\=expr/&g#c @@ -1650,11 +1667,11 @@ def Test_disassemble_invert_bool() '\d STORE $0\_s*' .. 'var invert = !flag\_s*' .. '\d LOAD $0\_s*' .. - '\d INVERT (!val)\_s*' .. + '\d INVERT -1 (!val)\_s*' .. '\d STORE $1\_s*' .. 'var res = !!flag\_s*' .. '\d LOAD $0\_s*' .. - '\d 2BOOL (!!val)\_s*' .. + '\d 2BOOL -1 (!!val)\_s*' .. '\d STORE $2\_s*', instr) assert_equal(true, InvertBool()) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 22186808e4..dc6858fc78 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -2480,6 +2480,25 @@ def Test_expr7_dict_vim9script() endif enddef +def Test_expr7_call_2bool() + var lines =<< trim END + vim9script + + def BrokenCall(nr: number, mode: bool, use: string): void + assert_equal(3, nr) + assert_equal(false, mode) + assert_equal('ab', use) + enddef + + def TestBrokenCall(): void + BrokenCall(3, 0, 'ab') + enddef + + TestBrokenCall() + END + CheckScriptSuccess(lines) +enddef + let g:oneString = 'one' def Test_expr_member() diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 3626cca47a..7e21b37883 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -2207,6 +2207,15 @@ def Test_legacy_lambda() CheckScriptSuccess(lines) enddef +def Test_legacy_errors() + for cmd in ['if', 'elseif', 'else', 'endif', + 'for', 'endfor', 'continue', 'break', + 'while', 'endwhile', + 'try', 'catch', 'finally', 'endtry'] + CheckDefFailure(['legacy ' .. cmd .. ' expr'], 'E1189:') + endfor +enddef + def DoFilterThis(a: string): list # closure nested inside another closure using argument var Filter = (l) => filter(l, (_, v) => stridx(v, a) == 0) @@ -2749,5 +2758,33 @@ def Test_closing_brace_at_start_of_line() call CheckDefAndScriptSuccess(lines) enddef +if has('python3') + def Test_python3_heredoc() + py3 << trim EOF + import vim + vim.vars['didit'] = 'yes' + EOF + assert_equal('yes', g:didit) + + python3 << trim EOF + import vim + vim.vars['didit'] = 'again' + EOF + assert_equal('again', g:didit) + enddef +endif + +" This messes up syntax highlight, keep near the end. +if has('lua') + def Test_lua_heredoc() + g:d = {} + lua << trim EOF + x = vim.eval('g:d') + x['key'] = 'val' + EOF + assert_equal('val', g:d.key) + enddef +endif + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index cdda613218..d5951c647a 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3074,6 +3074,27 @@ def Test_vim9_comment() 'func Test() # comment', 'endfunc', ], 'E488:') + + var lines =<< trim END + vim9script + syn region Text + \ start='foo' + #\ comment + \ end='bar' + syn region Text start='foo' + #\ comment + \ end='bar' + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + syn region Text + \ start='foo' + "\ comment + \ end='bar' + END + CheckScriptFailure(lines, 'E399:') enddef def Test_vim9_comment_gui() diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim index 1b3661c228..51ef5ae812 100644 --- a/src/testdir/test_visual.vim +++ b/src/testdir/test_visual.vim @@ -811,6 +811,10 @@ func Test_visual_block_mode() " reproducible if this operation is performed manually. "call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) call assert_equal(['aaxa', 'bbba', 'ccca'], getline(1, '$')) + " Repeat the previous test but use 'l' to move the cursor instead of '$' + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "normal! gg2l\2jA\x" + call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) " Change a characterwise motion to a blockwise motion using CTRL-V %d _ @@ -913,6 +917,15 @@ func Test_visual_block_mode() set tabstop& shiftwidth& endfunc +func Test_visual_force_motion_feedkeys() + onoremap i- execute('let g:mode = mode(1)')->slice(0, 0) + call feedkeys('dvi-', 'x') + call assert_equal('nov', g:mode) + call feedkeys('di-', 'x') + call assert_equal('no', g:mode) + ounmap i- +endfunc + " Test block-insert using cursor keys for movement func Test_visual_block_insert_cursor_keys() new diff --git a/src/time.c b/src/time.c index a28708f9f8..6d4ad5d51e 100644 --- a/src/time.c +++ b/src/time.c @@ -276,8 +276,7 @@ f_strftime(typval_T *argvars, typval_T *rettv) wp = enc_to_utf16(p, NULL); if (wp != NULL) - (void)wcsftime(result_buf, sizeof(result_buf) / sizeof(WCHAR), - wp, curtime); + (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime); else result_buf[0] = NUL; rettv->vval.v_string = utf16_to_enc(result_buf, NULL); diff --git a/src/typval.c b/src/typval.c index 3d1bd7f53f..a17dbef7ef 100644 --- a/src/typval.c +++ b/src/typval.c @@ -414,7 +414,7 @@ tv_get_string_strict(typval_T *varp) char_u * tv_get_string_buf(typval_T *varp, char_u *buf) { - char_u *res = tv_get_string_buf_chk(varp, buf); + char_u *res = tv_get_string_buf_chk(varp, buf); return res != NULL ? res : (char_u *)""; } @@ -462,8 +462,13 @@ tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict) break; case VAR_FLOAT: #ifdef FEAT_FLOAT - emsg(_(e_float_as_string)); - break; + if (strict) + { + emsg(_(e_float_as_string)); + break; + } + vim_snprintf((char *)buf, NUMBUFLEN, "%g", varp->vval.v_float); + return buf; #endif case VAR_STRING: if (varp->vval.v_string != NULL) @@ -478,44 +483,22 @@ tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict) break; case VAR_JOB: #ifdef FEAT_JOB_CHANNEL + if (in_vim9script()) { - job_T *job = varp->vval.v_job; - char *status; - - if (job == NULL) - return (char_u *)"no process"; - status = job->jv_status == JOB_FAILED ? "fail" - : job->jv_status >= JOB_ENDED ? "dead" - : "run"; -# ifdef UNIX - vim_snprintf((char *)buf, NUMBUFLEN, - "process %ld %s", (long)job->jv_pid, status); -# elif defined(MSWIN) - vim_snprintf((char *)buf, NUMBUFLEN, - "process %ld %s", - (long)job->jv_proc_info.dwProcessId, - status); -# else - // fall-back - vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status); -# endif - return buf; + semsg(_(e_using_invalid_value_as_string_str), "job"); + break; } + return job_to_string_buf(varp, buf); #endif break; case VAR_CHANNEL: #ifdef FEAT_JOB_CHANNEL + if (in_vim9script()) { - channel_T *channel = varp->vval.v_channel; - char *status = channel_status(channel, -1); - - if (channel == NULL) - vim_snprintf((char *)buf, NUMBUFLEN, "channel %s", status); - else - vim_snprintf((char *)buf, NUMBUFLEN, - "channel %d %s", channel->ch_id, status); - return buf; + semsg(_(e_using_invalid_value_as_string_str), "channel"); + break; } + return channel_to_string_buf(varp, buf); #endif break; case VAR_UNKNOWN: diff --git a/src/ui.c b/src/ui.c index c3de6e9ad1..b9d211979b 100644 --- a/src/ui.c +++ b/src/ui.c @@ -833,9 +833,10 @@ get_input_buf(void) /* * Restore the input buffer with a pointer returned from get_input_buf(). * The allocated memory is freed, this only works once! + * When "overwrite" is FALSE input typed later is kept. */ void -set_input_buf(char_u *p) +set_input_buf(char_u *p, int overwrite) { garray_T *gap = (garray_T *)p; @@ -843,8 +844,17 @@ set_input_buf(char_u *p) { if (gap->ga_data != NULL) { - mch_memmove(inbuf, gap->ga_data, gap->ga_len); - inbufcount = gap->ga_len; + if (overwrite || inbufcount + gap->ga_len >= INBUFLEN) + { + mch_memmove(inbuf, gap->ga_data, gap->ga_len); + inbufcount = gap->ga_len; + } + else + { + mch_memmove(inbuf + gap->ga_len, inbuf, inbufcount); + mch_memmove(inbuf, gap->ga_data, gap->ga_len); + inbufcount += gap->ga_len; + } vim_free(gap->ga_data); } vim_free(gap); diff --git a/src/usercmd.c b/src/usercmd.c index 03e7b245d3..a4bbfd77cf 100644 --- a/src/usercmd.c +++ b/src/usercmd.c @@ -339,7 +339,7 @@ get_user_cmd_flags(expand_T *xp UNUSED, int idx) "count", "nargs", "range", "register" }; - if (idx >= (int)(sizeof(user_cmd_flags) / sizeof(user_cmd_flags[0]))) + if (idx >= (int)ARRAY_LENGTH(user_cmd_flags)) return NULL; return (char_u *)user_cmd_flags[idx]; } @@ -352,7 +352,7 @@ get_user_cmd_nargs(expand_T *xp UNUSED, int idx) { static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"}; - if (idx >= (int)(sizeof(user_cmd_nargs) / sizeof(user_cmd_nargs[0]))) + if (idx >= (int)ARRAY_LENGTH(user_cmd_nargs)) return NULL; return (char_u *)user_cmd_nargs[idx]; } diff --git a/src/userfunc.c b/src/userfunc.c index f5d9063028..64e815d042 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -631,8 +631,12 @@ get_function_body( char_u *skip_until = NULL; int ret = FAIL; int is_heredoc = FALSE; + int heredoc_concat_len = 0; + garray_T heredoc_ga; char_u *heredoc_trimmed = NULL; + ga_init2(&heredoc_ga, 1, 500); + // Detect having skipped over comment lines to find the return // type. Add NULL lines to keep the line count correct. sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie); @@ -733,6 +737,20 @@ get_function_body( getline_options = vim9_function ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT; is_heredoc = FALSE; + + if (heredoc_concat_len > 0) + { + // Replace the starting line with all the concatenated + // lines. + ga_concat(&heredoc_ga, theline); + vim_free(((char_u **)(newlines->ga_data))[ + heredoc_concat_len - 1]); + ((char_u **)(newlines->ga_data))[ + heredoc_concat_len - 1] = heredoc_ga.ga_data; + ga_init(&heredoc_ga); + heredoc_concat_len = 0; + theline += STRLEN(theline); // skip the "EOF" + } } } } @@ -886,6 +904,8 @@ get_function_body( skip_until = vim_strnsave(p, skiptowhite(p) - p); getline_options = GETLINE_NONE; is_heredoc = TRUE; + if (eap->cmdidx == CMD_def) + heredoc_concat_len = newlines->ga_len + 1; } // Check for ":cmd v =<< [trim] EOF" @@ -928,10 +948,21 @@ get_function_body( if (ga_grow(newlines, 1 + sourcing_lnum_off) == FAIL) goto theend; - // Copy the line to newly allocated memory. get_one_sourceline() - // allocates 250 bytes per line, this saves 80% on average. The cost - // is an extra alloc/free. - p = vim_strsave(theline); + if (heredoc_concat_len > 0) + { + // For a :def function "python << EOF" concatenats all the lines, + // to be used for the instruction later. + ga_concat(&heredoc_ga, theline); + ga_concat(&heredoc_ga, (char_u *)"\n"); + p = vim_strsave((char_u *)""); + } + else + { + // Copy the line to newly allocated memory. get_one_sourceline() + // allocates 250 bytes per line, this saves 80% on average. The + // cost is an extra alloc/free. + p = vim_strsave(theline); + } if (p == NULL) goto theend; ((char_u **)(newlines->ga_data))[newlines->ga_len++] = p; @@ -953,6 +984,7 @@ get_function_body( theend: vim_free(skip_until); vim_free(heredoc_trimmed); + vim_free(heredoc_ga.ga_data); need_wait_return |= saved_wait_return; return ret; } @@ -1436,6 +1468,7 @@ deref_func_name( cc = name[*lenp]; name[*lenp] = NUL; + v = find_var(name, &ht, no_autoload); name[*lenp] = cc; if (v != NULL) diff --git a/src/version.c b/src/version.c index 5adf213fa4..0f03563eee 100644 --- a/src/version.c +++ b/src/version.c @@ -765,6 +765,88 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2961, +/**/ + 2960, +/**/ + 2959, +/**/ + 2958, +/**/ + 2957, +/**/ + 2956, +/**/ + 2955, +/**/ + 2954, +/**/ + 2953, +/**/ + 2952, +/**/ + 2951, +/**/ + 2950, +/**/ + 2949, +/**/ + 2948, +/**/ + 2947, +/**/ + 2946, +/**/ + 2945, +/**/ + 2944, +/**/ + 2943, +/**/ + 2942, +/**/ + 2941, +/**/ + 2940, +/**/ + 2939, +/**/ + 2938, +/**/ + 2937, +/**/ + 2936, +/**/ + 2935, +/**/ + 2934, +/**/ + 2933, +/**/ + 2932, +/**/ + 2931, +/**/ + 2930, +/**/ + 2929, +/**/ + 2928, +/**/ + 2927, +/**/ + 2926, +/**/ + 2925, +/**/ + 2924, +/**/ + 2923, +/**/ + 2922, +/**/ + 2921, /**/ 2920, /**/ @@ -6640,7 +6722,7 @@ has_patch(int n) // Perform a binary search. l = 0; - h = (int)(sizeof(included_patches) / sizeof(included_patches[0])) - 1; + h = (int)ARRAY_LENGTH(included_patches) - 1; while (l < h) { m = (l + h) / 2; @@ -6867,7 +6949,7 @@ list_version(void) { msg_puts(_("\nIncluded patches: ")); first = -1; - i = (int)(sizeof(included_patches) / sizeof(included_patches[0])) - 1; + i = (int)ARRAY_LENGTH(included_patches) - 1; while (--i >= 0) { if (first < 0) @@ -7167,7 +7249,7 @@ intro_message( #endif // blanklines = screen height - # message lines - blanklines = (int)Rows - ((sizeof(lines) / sizeof(char *)) - 1); + blanklines = (int)Rows - (ARRAY_LENGTH(lines) - 1); if (!p_cp) blanklines += 4; // add 4 for not showing "Vi compatible" message @@ -7186,7 +7268,7 @@ intro_message( row = blanklines / 2; if ((row >= 2 && Columns >= 50) || colon) { - for (i = 0; i < (int)(sizeof(lines) / sizeof(char *)); ++i) + for (i = 0; i < (int)ARRAY_LENGTH(lines); ++i) { p = lines[i]; #ifdef FEAT_GUI diff --git a/src/vim9.h b/src/vim9.h index b260bdf1f3..d4d3b7d6c1 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -14,6 +14,7 @@ typedef enum { ISN_EXEC, // execute Ex command line isn_arg.string ISN_EXECCONCAT, // execute Ex command from isn_arg.number items on stack + ISN_EXEC_SPLIT, // execute Ex command from isn_arg.string split at NL ISN_LEGACY_EVAL, // evaluate expression isn_arg.string with legacy syntax. ISN_ECHO, // echo isn_arg.echo.echo_count items on top of stack ISN_EXECUTE, // execute Ex commands isn_arg.number items on top of stack @@ -148,9 +149,9 @@ typedef enum { ISN_GETITEM, // push list item, isn_arg.number is the index ISN_MEMBER, // dict[member] ISN_STRINGMEMBER, // dict.member using isn_arg.string - ISN_2BOOL, // falsy/truthy to bool, invert if isn_arg.number != 0 + ISN_2BOOL, // falsy/truthy to bool, uses isn_arg.tobool ISN_COND2BOOL, // convert value to bool - ISN_2STRING, // convert value to string at isn_arg.number on stack + ISN_2STRING, // convert value to string at isn_arg.tostring on stack ISN_2STRING_ANY, // like ISN_2STRING but check type ISN_NEGATENR, // apply "-" to number @@ -369,6 +370,18 @@ typedef struct { cexprref_T *cexpr_ref; } cexpr_T; +// arguments to ISN_2STRING and ISN_2STRING_ANY +typedef struct { + int offset; + int tolerant; +} tostring_T; + +// arguments to ISN_2BOOL +typedef struct { + int offset; + int invert; +} tobool_T; + /* * Instruction */ @@ -414,6 +427,8 @@ struct isn_S { subs_T subs; cexpr_T cexpr; isn_T *instr; + tostring_T tostring; + tobool_T tobool; } isn_arg; }; diff --git a/src/vim9compile.c b/src/vim9compile.c index 2ea487de78..1256bfaac4 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -577,9 +577,10 @@ generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type) /* * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING. * But only for simple types. + * When "tolerant" is TRUE convert most types to string, e.g. a List. */ static int -may_generate_2STRING(int offset, cctx_T *cctx) +may_generate_2STRING(int offset, int tolerant, cctx_T *cctx) { isn_T *isn; isntype_T isntype = ISN_2STRING; @@ -606,12 +607,20 @@ may_generate_2STRING(int offset, cctx_T *cctx) isntype = ISN_2STRING_ANY; break; + // conversion possible when tolerant + case VAR_LIST: + if (tolerant) + { + isntype = ISN_2STRING_ANY; + break; + } + // FALLTHROUGH + // conversion not possible case VAR_VOID: case VAR_BLOB: case VAR_FUNC: case VAR_PARTIAL: - case VAR_LIST: case VAR_DICT: case VAR_JOB: case VAR_CHANNEL: @@ -623,7 +632,8 @@ may_generate_2STRING(int offset, cctx_T *cctx) *type = &t_string; if ((isn = generate_instr(cctx, isntype)) == NULL) return FAIL; - isn->isn_arg.number = offset; + isn->isn_arg.tostring.offset = offset; + isn->isn_arg.tostring.tolerant = tolerant; return OK; } @@ -886,9 +896,10 @@ generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic) /* * Generate an ISN_2BOOL instruction. + * "offset" is the offset in the type stack. */ static int -generate_2BOOL(cctx_T *cctx, int invert) +generate_2BOOL(cctx_T *cctx, int invert, int offset) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; @@ -896,10 +907,11 @@ generate_2BOOL(cctx_T *cctx, int invert) RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL) return FAIL; - isn->isn_arg.number = invert; + isn->isn_arg.tobool.invert = invert; + isn->isn_arg.tobool.offset = offset; // type becomes bool - ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool; + ((type_T **)stack->ga_data)[stack->ga_len + offset] = &t_bool; return OK; } @@ -1008,7 +1020,7 @@ need_type( { // Using "0", "1" or the result of an expression with "&&" or "||" as a // boolean is OK but requires a conversion. - generate_2BOOL(cctx, FALSE); + generate_2BOOL(cctx, FALSE, offset); return OK; } @@ -2782,7 +2794,7 @@ compile_member(int is_slice, cctx_T *cctx) return FAIL; *typep = &t_any; } - if (may_generate_2STRING(-1, cctx) == FAIL) + if (may_generate_2STRING(-1, FALSE, cctx) == FAIL) return FAIL; if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL) return FAIL; @@ -3625,7 +3637,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) } if (isn->isn_type == ISN_PUSHS) key = isn->isn_arg.string; - else if (may_generate_2STRING(-1, cctx) == FAIL) + else if (may_generate_2STRING(-1, FALSE, cctx) == FAIL) return FAIL; *arg = skipwhite(*arg); if (**arg != ']') @@ -4026,7 +4038,7 @@ compile_leader(cctx_T *cctx, int numeric_only, char_u *start, char_u **end) invert = !invert; --p; } - if (generate_2BOOL(cctx, invert) == FAIL) + if (generate_2BOOL(cctx, invert, -1) == FAIL) return FAIL; } } @@ -4849,8 +4861,8 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) ppconst->pp_is_const = FALSE; if (*op == '.') { - if (may_generate_2STRING(-2, cctx) == FAIL - || may_generate_2STRING(-1, cctx) == FAIL) + if (may_generate_2STRING(-2, FALSE, cctx) == FAIL + || may_generate_2STRING(-1, FALSE, cctx) == FAIL) return FAIL; generate_instr_drop(cctx, ISN_CONCAT, 1); } @@ -6420,7 +6432,8 @@ compile_assign_unlet( emsg(e_cannot_use_range_with_dictionary); return FAIL; } - if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL) + if (dest_type == VAR_DICT + && may_generate_2STRING(-1, FALSE, cctx) == FAIL) return FAIL; if (dest_type == VAR_LIST || dest_type == VAR_BLOB) { @@ -8383,7 +8396,7 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED) return NULL; if (cctx->ctx_skip == SKIP_YES) return p; - if (may_generate_2STRING(-1, cctx) == FAIL) + if (may_generate_2STRING(-1, FALSE, cctx) == FAIL) return NULL; if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL) return NULL; @@ -8523,13 +8536,30 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx) static char_u * compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx) { - char_u *p; - int has_expr = FALSE; - char_u *nextcmd = (char_u *)""; + char_u *p; + int has_expr = FALSE; + char_u *nextcmd = (char_u *)""; if (cctx->ctx_skip == SKIP_YES) goto theend; + // If there was a prececing command modifier, drop it and include it in the + // EXEC command. + if (cctx->ctx_has_cmdmod) + { + garray_T *instr = &cctx->ctx_instr; + isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; + + if (isn->isn_type == ISN_CMDMOD) + { + vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod + ->cmod_filter_regmatch.regprog); + vim_free(isn->isn_arg.cmdmod.cf_cmdmod); + --instr->ga_len; + cctx->ctx_has_cmdmod = FALSE; + } + } + if (eap->cmdidx >= 0 && eap->cmdidx < CMD_SIZE) { long argt = eap->argt; @@ -8618,7 +8648,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx) p += 2; if (compile_expr0(&p, cctx) == FAIL) return NULL; - may_generate_2STRING(-1, cctx); + may_generate_2STRING(-1, TRUE, cctx); ++count; p = skipwhite(p); if (*p != '`') @@ -8655,6 +8685,29 @@ theend: return nextcmd; } +/* + * A script command with heredoc, e.g. + * ruby << EOF + * command + * EOF + * Has been turned into one long line with NL characters by + * get_function_body(): + * ruby << EOF commandEOF + */ + static char_u * +compile_script(char_u *line, cctx_T *cctx) +{ + if (cctx->ctx_skip != SKIP_YES) + { + isn_T *isn; + + if ((isn = generate_instr(cctx, ISN_EXEC_SPLIT)) == NULL) + return NULL; + isn->isn_arg.string = vim_strsave(line); + } + return (char_u *)""; +} + /* * :s/pat/repl/ @@ -9222,6 +9275,27 @@ compile_def_function( { char_u *start = ea.cmd; + switch (ea.cmdidx) + { + case CMD_if: + case CMD_elseif: + case CMD_else: + case CMD_endif: + case CMD_for: + case CMD_endfor: + case CMD_continue: + case CMD_break: + case CMD_while: + case CMD_endwhile: + case CMD_try: + case CMD_catch: + case CMD_finally: + case CMD_endtry: + semsg(_(e_cannot_use_legacy_with_command_str), ea.cmd); + goto erret; + default: break; + } + // ":legacy return expr" needs to be handled differently. if (checkforcmd(&start, "return", 4)) ea.cmdidx = CMD_return; @@ -9446,18 +9520,28 @@ compile_def_function( line = (char_u *)""; break; - default: - if (cctx.ctx_skip == SKIP_YES) - { - // We don't check for a next command here. - line = (char_u *)""; - } - else - { - // Not recognized, execute with do_cmdline_cmd(). - ea.arg = p; + case CMD_lua: + case CMD_mzscheme: + case CMD_perl: + case CMD_py3: + case CMD_python3: + case CMD_python: + case CMD_pythonx: + case CMD_ruby: + case CMD_tcl: + ea.arg = p; + if (vim_strchr(line, '\n') == NULL) line = compile_exec(line, &ea, &cctx); - } + else + // heredoc lines have been concatenated with NL + // characters in get_function_body() + line = compile_script(line, &cctx); + break; + + default: + // Not recognized, execute with do_cmdline_cmd(). + ea.arg = p; + line = compile_exec(line, &ea, &cctx); break; } nextline: @@ -9640,6 +9724,7 @@ delete_instr(isn_T *isn) { case ISN_DEF: case ISN_EXEC: + case ISN_EXEC_SPLIT: case ISN_LEGACY_EVAL: case ISN_LOADAUTO: case ISN_LOADB: diff --git a/src/vim9execute.c b/src/vim9execute.c index 35a106f26f..5a8a112a6a 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -980,7 +980,7 @@ store_var(char_u *name, typval_T *tv) * Return FAIL if not allowed. */ static int -do_2string(typval_T *tv, int is_2string_any) +do_2string(typval_T *tv, int is_2string_any, int tolerant) { if (tv->v_type != VAR_STRING) { @@ -995,6 +995,41 @@ do_2string(typval_T *tv, int is_2string_any) case VAR_NUMBER: case VAR_FLOAT: case VAR_BLOB: break; + + case VAR_LIST: + if (tolerant) + { + char_u *s, *e, *p; + garray_T ga; + + ga_init2(&ga, sizeof(char_u *), 1); + + // Convert to NL separated items, then + // escape the items and replace the NL with + // a space. + str = typval2string(tv, TRUE); + if (str == NULL) + return FAIL; + s = str; + while ((e = vim_strchr(s, '\n')) != NULL) + { + *e = NUL; + p = vim_strsave_fnameescape(s, FALSE); + if (p != NULL) + { + ga_concat(&ga, p); + ga_concat(&ga, (char_u *)" "); + vim_free(p); + } + s = e + 1; + } + vim_free(str); + clear_tv(tv); + tv->v_type = VAR_STRING; + tv->vval.v_string = ga.ga_data; + return OK; + } + // FALLTHROUGH default: to_string_error(tv->v_type); return FAIL; } @@ -1178,6 +1213,37 @@ get_script_svar(scriptref_T *sref, ectx_T *ectx) return sv; } +/* + * Function passed to do_cmdline() for splitting a script joined by NL + * characters. + */ + static char_u * +get_split_sourceline( + int c UNUSED, + void *cookie, + int indent UNUSED, + getline_opt_T options UNUSED) +{ + source_cookie_T *sp = (source_cookie_T *)cookie; + char_u *p; + char_u *line; + + if (*sp->nextline == NUL) + return NULL; + p = vim_strchr(sp->nextline, '\n'); + if (p == NULL) + { + line = vim_strsave(sp->nextline); + sp->nextline += STRLEN(sp->nextline); + } + else + { + line = vim_strnsave(sp->nextline, p - sp->nextline); + sp->nextline = p + 1; + } + return line; +} + /* * Execute a function by "name". * This can be a builtin function, user function or a funcref. @@ -1390,6 +1456,30 @@ exec_instructions(ectx_T *ectx) } break; + // execute Ex command line split at NL characters. + case ISN_EXEC_SPLIT: + { + source_cookie_T cookie; + char_u *line; + + SOURCING_LNUM = iptr->isn_lnum; + CLEAR_FIELD(cookie); + cookie.sourcing_lnum = iptr->isn_lnum - 1; + cookie.nextline = iptr->isn_arg.string; + line = get_split_sourceline(0, &cookie, 0, 0); + if (do_cmdline(line, + get_split_sourceline, &cookie, + DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED) + == FAIL + || did_emsg) + { + vim_free(line); + goto on_error; + } + vim_free(line); + } + break; + // Evaluate an expression with legacy syntax, push it onto the // stack. case ISN_LEGACY_EVAL: @@ -2055,7 +2145,7 @@ exec_instructions(ectx_T *ectx) { dest_type = tv_dest->v_type; if (dest_type == VAR_DICT) - status = do_2string(tv_idx, TRUE); + status = do_2string(tv_idx, TRUE, FALSE); else if (dest_type == VAR_LIST && tv_idx->v_type != VAR_NUMBER) { @@ -3770,15 +3860,16 @@ exec_instructions(ectx_T *ectx) int n; int error = FALSE; - tv = STACK_TV_BOT(-1); if (iptr->isn_type == ISN_2BOOL) { + tv = STACK_TV_BOT(iptr->isn_arg.tobool.offset); n = tv2bool(tv); - if (iptr->isn_arg.number) // invert + if (iptr->isn_arg.tobool.invert) n = !n; } else { + tv = STACK_TV_BOT(-1); SOURCING_LNUM = iptr->isn_lnum; n = tv_get_bool_chk(tv, &error); if (error) @@ -3793,8 +3884,9 @@ exec_instructions(ectx_T *ectx) case ISN_2STRING: case ISN_2STRING_ANY: SOURCING_LNUM = iptr->isn_lnum; - if (do_2string(STACK_TV_BOT(iptr->isn_arg.number), - iptr->isn_type == ISN_2STRING_ANY) == FAIL) + if (do_2string(STACK_TV_BOT(iptr->isn_arg.tostring.offset), + iptr->isn_type == ISN_2STRING_ANY, + iptr->isn_arg.tostring.tolerant) == FAIL) goto on_error; break; @@ -4093,7 +4185,7 @@ exe_substitute_instr(void) { typval_T *tv = STACK_TV_BOT(-1); - res = vim_strsave(tv_get_string(tv)); + res = typval2string(tv, TRUE); --ectx->ec_stack.ga_len; clear_tv(tv); } @@ -4197,6 +4289,15 @@ call_def_function( semsg(_(e_nr_arguments_too_many), idx); goto failed_early; } + idx = argc - ufunc->uf_args.ga_len + ufunc->uf_def_args.ga_len; + if (idx < 0) + { + if (idx == -1) + emsg(_(e_one_argument_too_few)); + else + semsg(_(e_nr_arguments_too_few), -idx); + goto failed_early; + } // Put arguments on the stack, but no more than what the function expects. // A lambda can be called with more arguments than it uses. @@ -4490,6 +4591,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc) case ISN_EXEC: smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string); break; + case ISN_EXEC_SPLIT: + smsg("%s%4d EXEC_SPLIT %s", pfx, current, iptr->isn_arg.string); + break; case ISN_LEGACY_EVAL: smsg("%s%4d EVAL legacy %s", pfx, current, iptr->isn_arg.string); @@ -4785,10 +4889,11 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc) { typval_T tv; char_u *name; + char_u buf[NUMBUFLEN]; tv.v_type = VAR_JOB; tv.vval.v_job = iptr->isn_arg.job; - name = tv_get_string(&tv); + name = job_to_string_buf(&tv, buf); smsg("%s%4d PUSHJOB \"%s\"", pfx, current, name); } #endif @@ -5122,26 +5227,30 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc) break; } case ISN_COND2BOOL: smsg("%s%4d COND2BOOL", pfx, current); break; - case ISN_2BOOL: if (iptr->isn_arg.number) - smsg("%s%4d INVERT (!val)", pfx, current); + case ISN_2BOOL: if (iptr->isn_arg.tobool.invert) + smsg("%s%4d INVERT %d (!val)", pfx, current, + iptr->isn_arg.tobool.offset); else - smsg("%s%4d 2BOOL (!!val)", pfx, current); + smsg("%s%4d 2BOOL %d (!!val)", pfx, current, + iptr->isn_arg.tobool.offset); break; case ISN_2STRING: smsg("%s%4d 2STRING stack[%lld]", pfx, current, - (varnumber_T)(iptr->isn_arg.number)); + (varnumber_T)(iptr->isn_arg.tostring.offset)); break; - case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]", pfx, current, - (varnumber_T)(iptr->isn_arg.number)); + case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]", + pfx, current, + (varnumber_T)(iptr->isn_arg.tostring.offset)); break; - case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current, iptr->isn_arg.string); + case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current, + iptr->isn_arg.string); break; case ISN_PUT: if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE) smsg("%s%4d PUT %c above range", - pfx, current, iptr->isn_arg.put.put_regname); + pfx, current, iptr->isn_arg.put.put_regname); else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE) smsg("%s%4d PUT %c range", - pfx, current, iptr->isn_arg.put.put_regname); + pfx, current, iptr->isn_arg.put.put_regname); else smsg("%s%4d PUT %c %ld", pfx, current, iptr->isn_arg.put.put_regname,