From 00efded1064427ab3f84e4d57af62e0aab876fc6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 14:29:10 +0200 Subject: [PATCH 1/9] patch 7.4.1991 Problem: glob() does not add a symbolic link when there are no wildcards. Solution: Remove the call to mch_getperm(). --- src/misc1.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/misc1.c b/src/misc1.c index b5394a410d..a39658e38b 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -10864,7 +10864,7 @@ gen_expand_wildcards( * "vim c:/" work. */ if (flags & EW_NOTFOUND) addfile(&ga, t, flags | EW_DIR | EW_FILE); - else if (mch_getperm(t) >= 0) + else addfile(&ga, t, flags); vim_free(t); } diff --git a/src/version.c b/src/version.c index f86eccff08..c6ab5f4a8e 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1991, /**/ 1990, /**/ From e381d3d5e098546854b008e01ca1d28ba1a4a057 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 14:50:41 +0200 Subject: [PATCH 2/9] patch 7.4.1992 Problem: Values for true and false can be confusing. Solution: Update the documentation. Add a test. Make v:true evaluate to TRUE for a non-zero-arg. --- runtime/doc/eval.txt | 162 +++++++++++++++++--------------- src/Makefile | 3 +- src/eval.c | 10 +- src/testdir/test_alot.vim | 1 + src/testdir/test_true_false.vim | 125 ++++++++++++++++++++++++ src/version.c | 2 + 6 files changed, 225 insertions(+), 78 deletions(-) create mode 100644 src/testdir/test_true_false.vim diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 66dbd9a188..2e6e08a360 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Jul 04 +*eval.txt* For Vim version 7.4. Last change: 2016 Jul 06 VIM REFERENCE MANUAL by Bram Moolenaar @@ -100,14 +100,29 @@ To force conversion from String to Number, add zero to it: > To avoid a leading zero to cause octal conversion, or for using a different base, use |str2nr()|. + *TRUE* *FALSE* For boolean operators Numbers are used. Zero is FALSE, non-zero is TRUE. +You can also use |v:false| and |v:true|. When TRUE is returned from a +function it is the Number one, FALSE is the number zero. -Note that in the command > +Note that in the command: > :if "foo" -"foo" is converted to 0, which means FALSE. To test for a non-empty string, -use empty(): > + :" NOT executed +"foo" is converted to 0, which means FALSE. If the string starts with a +non-zero number it means TRUE: > + :if "8foo" + :" executed +To test for a non-empty string, use empty(): > :if !empty("foo") < + *non-zero-arg* +Function arguments often behave slightly different from |TRUE|: If the +argument is present and it evaluates to a non-zero Number, |v:true| or a +non-empty String, then the value is considere to be TRUE. +Note that " " and "0" are also non-empty strings, thus cause the mode to be +cleared. A List, Dictionary or Float is not a Number or String, thus +evaluates to FALSE. + *E745* *E728* *E703* *E729* *E730* *E731* *E908* *E910* *E913* List, Dictionary, Funcref and Job types are not automatically converted. @@ -694,7 +709,7 @@ expr1 *expr1* *E109* expr2 ? expr1 : expr1 The expression before the '?' is evaluated to a number. If it evaluates to -non-zero, the result is the value of the expression between the '?' and ':', +|TRUE|, the result is the value of the expression between the '?' and ':', otherwise the result is the value of the expression after the ':'. Example: > :echo lnum == 1 ? "top" : lnum @@ -722,12 +737,12 @@ expr2 and expr3 *expr2* *expr3* The "||" and "&&" operators take one argument on each side. The arguments are (converted to) Numbers. The result is: - input output ~ -n1 n2 n1 || n2 n1 && n2 ~ -zero zero zero zero -zero non-zero non-zero zero -non-zero zero non-zero zero -non-zero non-zero non-zero non-zero + input output ~ +n1 n2 n1 || n2 n1 && n2 ~ +|FALSE| |FALSE| |FALSE| |FALSE| +|FALSE| |TRUE| |TRUE| |FALSE| +|TRUE| |FALSE| |TRUE| |FALSE| +|TRUE| |TRUE| |TRUE| |TRUE| The operators can be concatenated, for example: > @@ -743,8 +758,8 @@ arguments are not evaluated. This is like what happens in C. For example: > let a = 1 echo a || b -This is valid even if there is no variable called "b" because "a" is non-zero, -so the result must be non-zero. Similarly below: > +This is valid even if there is no variable called "b" because "a" is |TRUE|, +so the result must be |TRUE|. Similarly below: > echo exists("b") && b == "yes" @@ -911,7 +926,7 @@ expr7 *expr7* - expr7 unary minus *expr-unary--* + expr7 unary plus *expr-unary-+* -For '!' non-zero becomes zero, zero becomes one. +For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one). For '-' the sign of the number is changed. For '+' the number is unchanged. @@ -1883,9 +1898,9 @@ atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2} browse({save}, {title}, {initdir}, {default}) String put up a file requester browsedir({title}, {initdir}) String put up a directory requester -bufexists({expr}) Number TRUE if buffer {expr} exists -buflisted({expr}) Number TRUE if buffer {expr} is listed -bufloaded({expr}) Number TRUE if buffer {expr} is loaded +bufexists({expr}) Number |TRUE| if buffer {expr} exists +buflisted({expr}) Number |TRUE| if buffer {expr} is listed +bufloaded({expr}) Number |TRUE| if buffer {expr} is loaded bufname({expr}) String Name of the buffer {expr} bufnr({expr} [, {create}]) Number Number of the buffer {expr} bufwinid({expr}) Number window ID of buffer {expr} @@ -1940,24 +1955,24 @@ cursor({lnum}, {col} [, {off}]) cursor({list}) Number move cursor to position in {list} deepcopy({expr} [, {noref}]) any make a full copy of {expr} delete({fname} [, {flags}]) Number delete the file or directory {fname} -did_filetype() Number TRUE if FileType autocommand event used +did_filetype() Number |TRUE| if FileType autocmd event used diff_filler({lnum}) Number diff filler lines about {lnum} diff_hlID({lnum}, {col}) Number diff highlighting at {lnum}/{col} -empty({expr}) Number TRUE if {expr} is empty +empty({expr}) Number |TRUE| if {expr} is empty escape({string}, {chars}) String escape {chars} in {string} with '\' eval({string}) any evaluate {string} into its value -eventhandler() Number TRUE if inside an event handler +eventhandler() Number |TRUE| if inside an event handler executable({expr}) Number 1 if executable {expr} exists exepath({expr}) String full path of the command {expr} -exists({expr}) Number TRUE if {expr} exists +exists({expr}) Number |TRUE| if {expr} exists extend({expr1}, {expr2} [, {expr3}]) List/Dict insert items of {expr2} into {expr1} exp({expr}) Float exponential of {expr} expand({expr} [, {nosuf} [, {list}]]) any expand special keywords in {expr} feedkeys({string} [, {mode}]) Number add key sequence to typeahead buffer -filereadable({file}) Number TRUE if {file} is a readable file -filewritable({file}) Number TRUE if {file} is a writable file +filereadable({file}) Number |TRUE| if {file} is a readable file +filewritable({file}) Number |TRUE| if {file} is a writable file filter({expr}, {string}) List/Dict remove items from {expr} where {string} is 0 finddir({name}[, {path}[, {count}]]) @@ -2022,17 +2037,17 @@ glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) glob2regpat({expr}) String convert a glob pat into a search pat globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]]) String do glob({expr}) for all dirs in {path} -has({feature}) Number TRUE if feature {feature} supported -has_key({dict}, {key}) Number TRUE if {dict} has entry {key} +has({feature}) Number |TRUE| if feature {feature} supported +has_key({dict}, {key}) Number |TRUE| if {dict} has entry {key} haslocaldir([{winnr} [, {tabnr}]]) - Number TRUE if the window executed |:lcd| + Number |TRUE| if the window executed |:lcd| hasmapto({what} [, {mode} [, {abbr}]]) - Number TRUE if mapping to {what} exists + Number |TRUE| if mapping to {what} exists histadd({history}, {item}) String add an item to a history histdel({history} [, {item}]) String remove an item from a history histget({history} [, {index}]) String get the item {index} from a history histnr({history}) Number highest index of a history -hlexists({name}) Number TRUE if highlight group {name} exists +hlexists({name}) Number |TRUE| if highlight group {name} exists hlID({name}) Number syntax ID of highlight group {name} hostname() String name of the machine Vim is running on iconv({expr}, {from}, {to}) String convert encoding of {expr} @@ -2049,9 +2064,9 @@ inputsave() Number save and clear typeahead inputsecret({prompt} [, {text}]) String like input() but hiding the text insert({list}, {item} [, {idx}]) List insert {item} in {list} [before {idx}] invert({expr}) Number bitwise invert -isdirectory({directory}) Number TRUE if {directory} is a directory -islocked({expr}) Number TRUE if {expr} is locked -isnan({expr}) Number TRUE if {expr} is NaN +isdirectory({directory}) Number |TRUE| if {directory} is a directory +islocked({expr}) Number |TRUE| if {expr} is locked +isnan({expr}) Number |TRUE| if {expr} is NaN items({dict}) List key-value pairs in {dict} job_getchannel({job}) Channel get the channel handle for {job} job_info({job}) Dict get information about {job} @@ -2438,7 +2453,7 @@ assert_notmatch({pattern}, {actual} [, {msg}]) assert_true({actual} [, {msg}]) *assert_true()* When {actual} is not true an error message is added to |v:errors|, like with |assert_equal()|. - A value is true when it is a non-zero number. When {actual} + A value is TRUE when it is a non-zero number. When {actual} is not a number the assert fails. When {msg} is omitted an error in the form "Expected True but got {actual}" is produced. @@ -2483,9 +2498,9 @@ atan2({expr1}, {expr2}) *atan2()* *browse()* browse({save}, {title}, {initdir}, {default}) Put up a file requester. This only works when "has("browse")" - returns non-zero (only in some GUI versions). + returns |TRUE| (only in some GUI versions). The input fields are: - {save} when non-zero, select file to write + {save} when |TRUE|, select file to write {title} title for the requester {initdir} directory to start browsing in {default} default file name @@ -2495,7 +2510,7 @@ browse({save}, {title}, {initdir}, {default}) *browsedir()* browsedir({title}, {initdir}) Put up a directory requester. This only works when - "has("browse")" returns non-zero (only in some GUI versions). + "has("browse")" returns |TRUE| (only in some GUI versions). On systems where a directory browser is not supported a file browser is used. In that case: select a file in the directory to be used. @@ -2506,7 +2521,7 @@ browsedir({title}, {initdir}) browsing is not possible, an empty string is returned. bufexists({expr}) *bufexists()* - The result is a Number, which is non-zero if a buffer called + The result is a Number, which is |TRUE| if a buffer called {expr} exists. If the {expr} argument is a number, buffer numbers are used. If the {expr} argument is a string it must match a buffer name @@ -2528,12 +2543,12 @@ bufexists({expr}) *bufexists()* Obsolete name: buffer_exists(). buflisted({expr}) *buflisted()* - The result is a Number, which is non-zero if a buffer called + The result is a Number, which is |TRUE| if a buffer called {expr} exists and is listed (has the 'buflisted' option set). The {expr} argument is used like with |bufexists()|. bufloaded({expr}) *bufloaded()* - The result is a Number, which is non-zero if a buffer called + The result is a Number, which is |TRUE| if a buffer called {expr} exists and is loaded (shown in a window or hidden). The {expr} argument is used like with |bufexists()|. @@ -2785,7 +2800,7 @@ complete_add({expr}) *complete_add()* complete_check() *complete_check()* Check for a key typed while looking for completion matches. This is to be used when looking for matches takes some time. - Returns non-zero when searching for matches is to be aborted, + Returns |TRUE| when searching for matches is to be aborted, zero otherwise. Only to be used by the function specified with the 'completefunc' option. @@ -3045,7 +3060,7 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()* in |List| or |Dictionary| {comp}. If {start} is given then start with the item with this index. {start} can only be used with a |List|. - When {ic} is given and it's non-zero then case is ignored. + When {ic} is given and it's |TRUE| then case is ignored. *cscope_connection()* @@ -3159,7 +3174,7 @@ delete({fname} [, {flags}]) *delete()* when the line number is in a variable. *did_filetype()* -did_filetype() Returns non-zero when autocommands are being executed and the +did_filetype() Returns |TRUE| when autocommands are being executed and the FileType event has been triggered at least once. Can be used to avoid triggering the FileType event again in the scripts that detect the file type. |FileType| @@ -3256,7 +3271,7 @@ exepath({expr}) *exepath()* an empty string is returned. *exists()* -exists({expr}) The result is a Number, which is non-zero if {expr} is +exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined, zero otherwise. The {expr} argument is a string, which contains one of these: &option-name Vim option (only checks if it exists, @@ -3353,7 +3368,7 @@ expand({expr} [, {nosuf} [, {list}]]) *expand()* Expand wildcards and the following special keywords in {expr}. 'wildignorecase' applies. - If {list} is given and it is non-zero, a List will be returned. + If {list} is given and it is |TRUE|, a List will be returned. Otherwise the result is a String and when there are several matches, they are separated by characters. [Note: in version 5.0 a space was used, which caused problems when a @@ -3412,7 +3427,7 @@ expand({expr} [, {nosuf} [, {list}]]) *expand()* When {expr} does not start with '%', '#' or '<', it is expanded like a file name is expanded on the command line. 'suffixes' and 'wildignore' are used, unless the optional - {nosuf} argument is given and it is non-zero. + {nosuf} argument is given and it is |TRUE|. Names for non-existing files are included. The "**" item can be used to search in a directory tree. For example, to find all "README" files in the current directory and below: > @@ -3504,9 +3519,9 @@ feedkeys({string} [, {mode}]) *feedkeys()* Return value is always 0. filereadable({file}) *filereadable()* - The result is a Number, which is TRUE when a file with the + The result is a Number, which is |TRUE| when a file with the name {file} exists, and can be read. If {file} doesn't exist, - or is a directory, the result is FALSE. {file} is any + or is a directory, the result is |FALSE|. {file} is any expression, which is used as a String. If you don't care about the file being readable you can use |glob()|. @@ -3545,7 +3560,7 @@ filter({expr1}, {expr2}) *filter()* If {expr2} is a |Funcref| it must take two arguments: 1. the key or the index of the current item. 2. the value of the current item. - The function must return TRUE if the item should be kept. + The function must return |TRUE| if the item should be kept. Example that keeps the odd items of a list: > func Odd(idx, val) return a:idx % 2 == 1 @@ -4185,13 +4200,13 @@ getqflist() *getqflist()* bufname() to get the name lnum line number in the buffer (first line is 1) col column number (first column is 1) - vcol non-zero: "col" is visual column - zero: "col" is byte index + vcol |TRUE|: "col" is visual column + |FALSE|: "col" is byte index nr error number pattern search pattern used to locate the error text description of the error type type of the error, 'E', '1', etc. - valid non-zero: recognized error message + valid |TRUE|: recognized error message When there is no error list or it's empty an empty list is returned. Quickfix list entries with non-existing buffer @@ -4217,7 +4232,7 @@ getreg([{regname} [, 1 [, {list}]]]) *getreg()* be restored with |setreg()|. For other registers the extra argument is ignored, thus you can always give it. - If {list} is present and non-zero, the result type is changed + If {list} is present and |TRUE|, the result type is changed to |List|. Each list item is one text line. Use it if you care about zero bytes possibly present inside register: without third argument both NLs and zero bytes are represented as NLs @@ -4288,13 +4303,13 @@ glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) *glob()* Expand the file wildcards in {expr}. See |wildcards| for the use of special characters. - Unless the optional {nosuf} argument is given and is non-zero, + Unless the optional {nosuf} argument is given and is |TRUE|, the 'suffixes' and 'wildignore' options apply: Names matching one of the patterns in 'wildignore' will be skipped and 'suffixes' affect the ordering of matches. 'wildignorecase' always applies. - When {list} is present and it is non-zero the result is a List + When {list} is present and it is |TRUE| the result is a List with all matching files. The advantage of using a List is, you also get filenames containing newlines correctly. Otherwise the result is a String and when there are several @@ -4305,7 +4320,7 @@ glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) *glob()* A name for a non-existing file is not included. A symbolic link is only included if it points to an existing file. However, when the {alllinks} argument is present and it is - non-zero then all symbolic links are included. + |TRUE| then all symbolic links are included. For most systems backticks can be used to get files names from any external command. Example: > @@ -4342,12 +4357,12 @@ globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]]) If the expansion fails for one of the directories, there is no error message. - Unless the optional {nosuf} argument is given and is non-zero, + Unless the optional {nosuf} argument is given and is |TRUE|, the 'suffixes' and 'wildignore' options apply: Names matching one of the patterns in 'wildignore' will be skipped and 'suffixes' affect the ordering of matches. - When {list} is present and it is non-zero the result is a List + When {list} is present and it is |TRUE| the result is a List with all matching files. The advantage of using a List is, you also get filenames containing newlines correctly. Otherwise the result is a String and when there are several matches, @@ -4390,7 +4405,7 @@ hasmapto({what} [, {mode} [, {abbr}]]) *hasmapto()* contains {what} in somewhere in the rhs (what it is mapped to) and this mapping exists in one of the modes indicated by {mode}. - When {abbr} is there and it is non-zero use abbreviations + When {abbr} is there and it is |TRUE| use abbreviations instead of mappings. Don't forget to specify Insert and/or Command-line mode. Both the global mappings and the mappings local to the current @@ -4549,7 +4564,7 @@ index({list}, {expr} [, {start} [, {ic}]]) *index()* is not used here, case always matters. If {start} is given then start looking at the item with index {start} (may be negative for an item relative to the end). - When {ic} is given and it is non-zero, ignore case. Otherwise + When {ic} is given and it is |TRUE|, ignore case. Otherwise case must match. -1 is returned when {expr} is not found in {list}. Example: > @@ -4677,13 +4692,13 @@ invert({expr}) *invert()* :let bits = invert(bits) isdirectory({directory}) *isdirectory()* - The result is a Number, which is non-zero when a directory + The result is a Number, which is |TRUE| when a directory with the name {directory} exists. If {directory} doesn't - exist, or isn't a directory, the result is FALSE. {directory} + exist, or isn't a directory, the result is |FALSE|. {directory} is any expression, which is used as a String. islocked({expr}) *islocked()* *E786* - The result is a Number, which is non-zero when {expr} is the + The result is a Number, which is |TRUE| when {expr} is the name of a locked variable. {expr} must be the name of a variable, |List| item or |Dictionary| entry, not the variable itself! Example: > @@ -4696,7 +4711,7 @@ islocked({expr}) *islocked()* *E786* message. Use |exists()| to check for existence. isnan({expr}) *isnan()* - Return non-zero if {expr} is a float with value NaN. > + Return |TRUE| if {expr} is a float with value NaN. > echo isnan(0.0 / 0.0) < 1 ~ @@ -5114,10 +5129,10 @@ maparg({name}[, {mode} [, {abbr} [, {dict}]]]) *maparg()* "" Normal, Visual and Operator-pending When {mode} is omitted, the modes for "" are used. - When {abbr} is there and it is non-zero use abbreviations + When {abbr} is there and it is |TRUE| use abbreviations instead of mappings. - When {dict} is there and it is non-zero return a dictionary + When {dict} is there and it is |TRUE| return a dictionary containing all the information of the mapping with the following items: "lhs" The {lhs} of the mapping. @@ -5148,7 +5163,7 @@ mapcheck({name}[, {mode} [, {abbr}]]) *mapcheck()* Check if there is a mapping that matches with {name} in mode {mode}. See |maparg()| for {mode} and special names in {name}. - When {abbr} is there and it is non-zero use abbreviations + When {abbr} is there and it is |TRUE| use abbreviations instead of mappings. A match happens with a mapping that starts with {name} and with a mapping which is equal to the start of {name}. @@ -5430,8 +5445,7 @@ mkdir({name} [, {path} [, {prot}]]) mode([expr]) Return a string that indicates the current mode. If [expr] is supplied and it evaluates to a non-zero Number or a non-empty String (|non-zero-arg|), then the full mode is - returned, otherwise only the first letter is returned. Note - that " " and "0" are also non-empty strings. + returned, otherwise only the first letter is returned. n Normal no Operator-pending @@ -6541,8 +6555,8 @@ shellescape({string} [, {special}]) *shellescape()* On MS-Windows and MS-DOS, when 'shellslash' is not set, it will enclose {string} in double quotes and double all double quotes within {string}. - For other systems, it will enclose {string} in single quotes - and replace all "'" with "'\''". + Otherwise it will enclose {string} in single quotes and + replace all "'" with "'\''". When the {special} argument is present and it's a non-zero Number or a non-empty String (|non-zero-arg|), then special items such as "!", "%", "#" and "" will be preceded by @@ -7294,7 +7308,7 @@ test_alloc_fail({id}, {countdown}, {repeat}) *test_alloc_fail()* *test_disable_char_avail()* test_disable_char_avail({expr}) When {expr} is 1 the internal char_avail() function will - return FALSE. When {expr} is 0 the char_avail() function will + return |FALSE|. When {expr} is 0 the char_avail() function will function normally. Only use this for a test where typeahead causes the test not to work. E.g., to trigger the CursorMovedI autocommand event. @@ -7543,16 +7557,12 @@ visualmode([expr]) *visualmode()* Visual mode that was used. If Visual mode is active, use |mode()| to get the Visual mode (e.g., in a |:vmap|). - *non-zero-arg* If [expr] is supplied and it evaluates to a non-zero Number or a non-empty String, then the Visual mode will be cleared and - the old value is returned. Note that " " and "0" are also - non-empty strings, thus cause the mode to be cleared. A List, - Dictionary or Float is not a Number or String, thus does not - cause the mode to be cleared. + the old value is returned. See |non-zero-arg|. wildmenumode() *wildmenumode()* - Returns non-zero when the wildmenu is active and zero + Returns |TRUE| when the wildmenu is active and |FALSE| otherwise. See 'wildmenu' and 'wildmode'. This can be used in mappings to handle the 'wildcharm' option gracefully. (Makes only sense with |mapmode-c| mappings). diff --git a/src/Makefile b/src/Makefile index 4deb4a2bff..6add935913 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2068,11 +2068,12 @@ test_arglist \ test_statusline \ test_syn_attr \ test_syntax \ - test_usercommands \ test_tabline \ test_tagjump \ test_timers \ + test_true_false \ test_undolevels \ + test_usercommands \ test_unlet \ test_viminfo \ test_viml \ diff --git a/src/eval.c b/src/eval.c index 4a1ad4f73a..69238c1813 100644 --- a/src/eval.c +++ b/src/eval.c @@ -9414,6 +9414,8 @@ non_zero_arg(typval_T *argvars) { return ((argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number != 0) + || (argvars[0].v_type == VAR_SPECIAL + && argvars[0].vval.v_number == VVAL_TRUE) || (argvars[0].v_type == VAR_STRING && argvars[0].vval.v_string != NULL && *argvars[0].vval.v_string != NUL)); @@ -16350,7 +16352,13 @@ f_mode(typval_T *argvars, typval_T *rettv) buf[1] = NUL; buf[2] = NUL; - if (VIsual_active) + if (time_for_testing == 93784) + { + /* Testing the two-character code. */ + buf[0] = 'x'; + buf[1] = '!'; + } + else if (VIsual_active) { if (VIsual_select) buf[0] = VIsual_mode + 's' - 'v'; diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim index e8546efe8a..e99e137640 100644 --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -33,6 +33,7 @@ source test_syn_attr.vim source test_tabline.vim source test_tagjump.vim source test_timers.vim +source test_true_false.vim source test_undolevels.vim source test_unlet.vim source test_window_cmd.vim diff --git a/src/testdir/test_true_false.vim b/src/testdir/test_true_false.vim new file mode 100644 index 0000000000..90b4f5e37a --- /dev/null +++ b/src/testdir/test_true_false.vim @@ -0,0 +1,125 @@ +" Test behavior of boolean-like values. + +" Test what is explained at ":help TRUE" and ":help FALSE". +func Test_if() + if v:false + call assert_true(false, 'v:false is false') + endif + if 0 + call assert_true(false, 'zero is false') + endif + if "0" + call assert_true(false, 'zero string is false') + endif + if "foo" + call assert_true(false, 'foo is false') + endif + if " " + call assert_true(false, 'space is false') + endif + if empty("foo") + call assert_true(false, 'foo is not empty') + endif + + if v:true + else + call assert_true(false, 'v:true is true') + endif + if 1 + else + call assert_true(false, 'one is true') + endif + if "1" + else + call assert_true(false, 'one string is true') + endif + if "1foo" + else + call assert_true(false, 'one in string is true') + endif + + call assert_fails('if [1]', 'E745') + call assert_fails('if {1: 1}', 'E728') + call assert_fails('if function("string")', 'E703') + call assert_fails('if 1.3")', 'E805') +endfunc + +function Try_arg_true_false(expr, false_val, true_val) + for v in ['v:false', '0', '"0"', '"foo"', '" "'] + let r = eval(substitute(a:expr, '%v%', v, '')) + call assert_equal(a:false_val, r, 'result for ' . v . ' is not ' . string(a:false_val) . ' but ' . string(r)) + endfor + for v in ['v:true', '1', '"1"', '"1foo"'] + let r = eval(substitute(a:expr, '%v%', v, '')) + call assert_equal(a:true_val, r, 'result for ' . v . ' is not ' . string(a:true_val) . ' but ' . string(r)) + endfor +endfunc + +" Test using TRUE or FALSE values for an argument. +func Test_true_false_arg() + call Try_arg_true_false('count(["a", "A"], "a", %v%)', 1, 2) + + set wildignore=*.swp + call Try_arg_true_false('expand("foo.swp", %v%)', "", "foo.swp") + call Try_arg_true_false('expand("foo.vim", 0, %v%)', "foo.vim", ["foo.vim"]) + + call setreg('a', ['x', 'y']) + call Try_arg_true_false('getreg("a", 1, %v%)', "x\ny\n", ['x', 'y']) + + set wildignore=*.vim + call Try_arg_true_false('glob("runtest.vim", %v%)', "", "runtest.vim") + set wildignore=*.swp + call Try_arg_true_false('glob("runtest.vim", 0, %v%)', "runtest.vim", ["runtest.vim"]) + if has('unix') + silent !ln -s doesntexit Xlink + call Try_arg_true_false('glob("Xlink", 0, 0, %v%)', "", "Xlink") + silent !rm Xlink + endif + + set wildignore=*.vim + call Try_arg_true_false('globpath(".", "runtest.vim", %v%)', "", "./runtest.vim") + set wildignore=*.swp + call Try_arg_true_false('globpath(".", "runtest.vim", 0, %v%)', "./runtest.vim", ["./runtest.vim"]) + if has('unix') + silent !ln -s doesntexit Xlink + call Try_arg_true_false('globpath(".", "Xlink", 0, 0, %v%)', "", "./Xlink") + silent !rm Xlink + endif +endfunc + +function Try_arg_non_zero(expr, false_val, true_val) + for v in ['v:false', '0', '[1]', '{2:3}', '3.4'] + let r = eval(substitute(a:expr, '%v%', v, '')) + call assert_equal(a:false_val, r, 'result for ' . v . ' is not ' . a:false_val . ' but ' . r) + endfor + for v in ['v:true', '1', '" "', '"0"'] + let r = eval(substitute(a:expr, '%v%', v, '')) + call assert_equal(a:true_val, r, 'result for ' . v . ' is not ' . a:true_val . ' but ' . r) + endfor +endfunc + + +" Test using non-zero-arg for an argument. +func Test_non_zero_arg() + call test_settime(93784) + call Try_arg_non_zero("mode(%v%)", 'x', 'x!') + call test_settime(0) + + call Try_arg_non_zero("shellescape('foo%', %v%)", "'foo%'", "'foo\\%'") + + " visualmode() needs to be called twice to check + for v in [v:false, 0, [1], {2:3}, 3.4] + normal vv + let r = visualmode(v) + call assert_equal('v', r, 'result for ' . string(v) . ' is not "v" but ' . r) + let r = visualmode(v) + call assert_equal('v', r, 'result for ' . string(v) . ' is not "v" but ' . r) + endfor + for v in [v:true, 1, " ", "0"] + normal vv + let r = visualmode(v) + call assert_equal('v', r, 'result for ' . v . ' is not "v" but ' . r) + let r = visualmode(v) + call assert_equal('', r, 'result for ' . v . ' is not "" but ' . r) + endfor +endfunc diff --git a/src/version.c b/src/version.c index c6ab5f4a8e..f235d762b4 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1992, /**/ 1991, /**/ From 6bb450145e96d7b182769fd9502a267da72667ec Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 15:11:19 +0200 Subject: [PATCH 3/9] patch 7.4.1993 Problem: Not all TRUE and FALSE arguments are tested. Solution: Add a few more tests. --- src/testdir/test_true_false.vim | 19 +++++++++++++++++++ src/version.c | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/testdir/test_true_false.vim b/src/testdir/test_true_false.vim index 90b4f5e37a..f11d298ee6 100644 --- a/src/testdir/test_true_false.vim +++ b/src/testdir/test_true_false.vim @@ -85,6 +85,25 @@ func Test_true_false_arg() call Try_arg_true_false('globpath(".", "Xlink", 0, 0, %v%)', "", "./Xlink") silent !rm Xlink endif + + abbr asdf asdff + call Try_arg_true_false('hasmapto("asdff", "i", %v%)', 0, 1) + + call Try_arg_true_false('index(["a", "A"], "A", 0, %v%)', 1, 0) + + call Try_arg_true_false('maparg("asdf", "i", %v%)', "", "asdff") + call Try_arg_true_false('maparg("asdf", "i", 1, %v%)', "asdff", {'silent': 0, 'noremap': 0, 'lhs': 'asdf', 'mode': '!', 'nowait': 0, 'expr': 0, 'sid': 3, 'rhs': 'asdff', 'buffer': 0}) + + call Try_arg_true_false('hasmapto("asdf", "i", %v%)', 0, 1) + + new colored + call setline(1, '') + syn match brackets "<.*>" + syn match here "here" transparent + let brackets_id = synID(1, 1, 0) + let here_id = synID(1, 3, 0) + call Try_arg_true_false('synID(1, 3, %v%)', here_id, brackets_id) + bwipe! endfunc function Try_arg_non_zero(expr, false_val, true_val) diff --git a/src/version.c b/src/version.c index f235d762b4..996745596b 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1993, /**/ 1992, /**/ From 05e418d436410cd8bbf5a29ff81e8ad68408b1e8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 16:35:16 +0200 Subject: [PATCH 4/9] patch 7.4.1994 Problem: True-false test fails. Solution: Filter the dict to only keep the value that matters. --- src/testdir/test_true_false.vim | 8 +++++++- src/version.c | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_true_false.vim b/src/testdir/test_true_false.vim index f11d298ee6..a23f2cc4b1 100644 --- a/src/testdir/test_true_false.vim +++ b/src/testdir/test_true_false.vim @@ -91,8 +91,14 @@ func Test_true_false_arg() call Try_arg_true_false('index(["a", "A"], "A", 0, %v%)', 1, 0) + function FilterMapArg(d) + if type(a:d) == type({}) + return filter(a:d, 'v:key == "rhs"') + endif + return a:d + endfunction call Try_arg_true_false('maparg("asdf", "i", %v%)', "", "asdff") - call Try_arg_true_false('maparg("asdf", "i", 1, %v%)', "asdff", {'silent': 0, 'noremap': 0, 'lhs': 'asdf', 'mode': '!', 'nowait': 0, 'expr': 0, 'sid': 3, 'rhs': 'asdff', 'buffer': 0}) + call Try_arg_true_false('FilterMapArg(maparg("asdf", "i", 1, %v%))', "asdff", {'rhs': 'asdff'}) call Try_arg_true_false('hasmapto("asdf", "i", %v%)', 0, 1) diff --git a/src/version.c b/src/version.c index 996745596b..8bb497d027 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1994, /**/ 1993, /**/ From 9d5d3c9c4468ad76f16b50eabd3d9e7eab2ed44d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 16:43:02 +0200 Subject: [PATCH 5/9] patch 7.4.1995 Problem: GUI: cursor drawn in wrong place if a timer callback causes a screen update. (David Samvelyan) Solution: Also redraw the cursor when it's blinking and on. --- src/gui_gtk_x11.c | 6 ++++++ src/gui_mac.c | 6 ++++++ src/gui_photon.c | 6 ++++++ src/gui_w32.c | 6 ++++++ src/gui_x11.c | 6 ++++++ src/proto/gui_gtk_x11.pro | 1 + src/proto/gui_mac.pro | 1 + src/proto/gui_photon.pro | 1 + src/proto/gui_w32.pro | 1 + src/proto/gui_x11.pro | 1 + src/screen.c | 6 +++--- src/version.c | 2 ++ 12 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 620c892283..39b918ab83 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -818,6 +818,12 @@ gui_mch_is_blinking(void) return blink_state != BLINK_NONE; } + int +gui_mch_is_blink_off(void) +{ + return blink_state == BLINK_OFF; +} + void gui_mch_set_blinking(long waittime, long on, long off) { diff --git a/src/gui_mac.c b/src/gui_mac.c index 06f92044b9..b2e4d4b3f7 100644 --- a/src/gui_mac.c +++ b/src/gui_mac.c @@ -5116,6 +5116,12 @@ gui_mch_destroy_scrollbar(scrollbar_T *sb) int gui_mch_is_blinking(void) +{ + return FALSE; +} + + int +gui_mch_is_blink_off(void) { return FALSE; } diff --git a/src/gui_photon.c b/src/gui_photon.c index a4d00f7830..1000a5eb5a 100644 --- a/src/gui_photon.c +++ b/src/gui_photon.c @@ -2238,6 +2238,12 @@ gui_mch_is_blinking(void) return blink_state != BLINK_NONE; } + int +gui_mch_is_blink_off(void) +{ + return blink_state == BLINK_OFF; +} + void gui_mch_set_blinking(long wait, long on, long off) { diff --git a/src/gui_w32.c b/src/gui_w32.c index cf29b610c3..19f4d65002 100644 --- a/src/gui_w32.c +++ b/src/gui_w32.c @@ -546,6 +546,12 @@ gui_mch_is_blinking(void) return blink_state != BLINK_NONE; } + int +gui_mch_is_blink_off(void) +{ + return blink_state == BLINK_OFF; +} + void gui_mch_set_blinking(long wait, long on, long off) { diff --git a/src/gui_x11.c b/src/gui_x11.c index deed76e9e5..3ccc92a553 100644 --- a/src/gui_x11.c +++ b/src/gui_x11.c @@ -3161,6 +3161,12 @@ gui_mch_is_blinking(void) return blink_state != BLINK_NONE; } + int +gui_mch_is_blink_off(void) +{ + return blink_state == BLINK_OFF; +} + void gui_mch_set_blinking(long waittime, long on, long off) { diff --git a/src/proto/gui_gtk_x11.pro b/src/proto/gui_gtk_x11.pro index 3735f8d315..b79feb8e4e 100644 --- a/src/proto/gui_gtk_x11.pro +++ b/src/proto/gui_gtk_x11.pro @@ -2,6 +2,7 @@ void gui_mch_prepare(int *argc, char **argv); void gui_mch_free_all(void); int gui_mch_is_blinking(void); +int gui_mch_is_blink_off(void); void gui_mch_set_blinking(long waittime, long on, long off); void gui_mch_stop_blink(void); void gui_mch_start_blink(void); diff --git a/src/proto/gui_mac.pro b/src/proto/gui_mac.pro index ff837594b1..ebe13f3300 100644 --- a/src/proto/gui_mac.pro +++ b/src/proto/gui_mac.pro @@ -15,6 +15,7 @@ void gui_mac_focus_change(EventRecord *event); void gui_mac_update(EventRecord *event); short gui_mch_get_mac_menu_item_index(vimmenu_T *menu, vimmenu_T *parent); int gui_mch_is_blinking(void); +int gui_mch_is_blink_off(void); void gui_mch_set_blinking(long wait, long on, long off); void gui_mch_stop_blink(void); void gui_mch_start_blink(void); diff --git a/src/proto/gui_photon.pro b/src/proto/gui_photon.pro index 46d73b7419..b03479c974 100644 --- a/src/proto/gui_photon.pro +++ b/src/proto/gui_photon.pro @@ -40,6 +40,7 @@ void gui_mch_draw_string(int row, int col, char_u *s, int len, int flags); void gui_mch_draw_hollow_cursor(guicolor_T color); void gui_mch_draw_part_cursor(int w, int h, guicolor_T color); int gui_mch_is_blinking(void); +int gui_mch_is_blink_off(void); void gui_mch_set_blinking(long wait, long on, long off); void gui_mch_start_blink(void); void gui_mch_stop_blink(void); diff --git a/src/proto/gui_w32.pro b/src/proto/gui_w32.pro index e35373c609..20a92b9746 100644 --- a/src/proto/gui_w32.pro +++ b/src/proto/gui_w32.pro @@ -2,6 +2,7 @@ int directx_enabled(void); int gui_mch_set_rendering_options(char_u *s); int gui_mch_is_blinking(void); +int gui_mch_is_blink_off(void); void gui_mch_set_blinking(long wait, long on, long off); void gui_mch_stop_blink(void); void gui_mch_start_blink(void); diff --git a/src/proto/gui_x11.pro b/src/proto/gui_x11.pro index 2d2eafadeb..ccaf99fd48 100644 --- a/src/proto/gui_x11.pro +++ b/src/proto/gui_x11.pro @@ -54,6 +54,7 @@ void gui_mch_menu_hidden(vimmenu_T *menu, int hidden); void gui_mch_draw_menubar(void); void gui_x11_menu_cb(Widget w, XtPointer client_data, XtPointer call_data); int gui_mch_is_blinking(void); +int gui_mch_is_blink_off(void); void gui_mch_set_blinking(long waittime, long on, long off); void gui_mch_stop_blink(void); void gui_mch_start_blink(void); diff --git a/src/screen.c b/src/screen.c index 684401cd29..2f08afb7a8 100644 --- a/src/screen.c +++ b/src/screen.c @@ -432,9 +432,9 @@ redraw_after_callback(void) #ifdef FEAT_GUI if (gui.in_use) { - /* Don't update the cursor while it is blinking, it will get - * updated soon and this avoids interrupting the blinking. */ - if (!gui_mch_is_blinking()) + /* Don't update the cursor when it is blinking and off to avoid + * flicker. */ + if (!gui_mch_is_blink_off()) gui_update_cursor(FALSE, FALSE); gui_mch_flush(); } diff --git a/src/version.c b/src/version.c index 8bb497d027..f34f7a0a79 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1995, /**/ 1994, /**/ From 1e5e1231ac9e1ba9678812c96f9d554a078eeec4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 17:33:02 +0200 Subject: [PATCH 6/9] patch 7.4.1996 Problem: Capturing the output of a command takes a few commands. Solution: Add evalcmd(). --- runtime/doc/eval.txt | 10 +++++++++ src/Makefile | 1 + src/eval.c | 39 ++++++++++++++++++++++++++++++++++++ src/testdir/test_alot.vim | 3 ++- src/testdir/test_evalcmd.vim | 8 ++++++++ src/version.c | 2 ++ 6 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/testdir/test_evalcmd.vim diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2e6e08a360..4b55f911d0 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1961,6 +1961,7 @@ diff_hlID({lnum}, {col}) Number diff highlighting at {lnum}/{col} empty({expr}) Number |TRUE| if {expr} is empty escape({string}, {chars}) String escape {chars} in {string} with '\' eval({string}) any evaluate {string} into its value +evalcmd({command}) String execute {command} and get the output eventhandler() Number |TRUE| if inside an event handler executable({expr}) Number 1 if executable {expr} exists exepath({expr}) String full path of the command {expr} @@ -3231,6 +3232,15 @@ eval({string}) Evaluate {string} and return the result. Especially useful to them. Also works for |Funcref|s that refer to existing functions. +evalcmd({command}) *evalcmd()* + Execute Ex {command} and return the output as a string. This + is equivalent to: > + redir => var + {command} + redir END +< To get a list of lines use: > + split(evalcmd(cmd), "\n") + eventhandler() *eventhandler()* Returns 1 when inside an event handler. That is that Vim got interrupted while waiting for the user to type a character, diff --git a/src/Makefile b/src/Makefile index 6add935913..13bf776914 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2023,6 +2023,7 @@ test_arglist \ test_cmdline \ test_cursor_func \ test_delete \ + test_evalcmd \ test_ex_undo \ test_expand \ test_expand_dllpath \ diff --git a/src/eval.c b/src/eval.c index 69238c1813..74ed263d51 100644 --- a/src/eval.c +++ b/src/eval.c @@ -555,6 +555,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv); static void f_empty(typval_T *argvars, typval_T *rettv); static void f_escape(typval_T *argvars, typval_T *rettv); static void f_eval(typval_T *argvars, typval_T *rettv); +static void f_evalcmd(typval_T *argvars, typval_T *rettv); static void f_eventhandler(typval_T *argvars, typval_T *rettv); static void f_executable(typval_T *argvars, typval_T *rettv); static void f_exepath(typval_T *argvars, typval_T *rettv); @@ -1133,6 +1134,7 @@ set_internal_string_var(char_u *name, char_u *value) } static lval_T *redir_lval = NULL; +#define EVALCMD_BUSY (redir_lval == (lval_T *)&redir_lval) static garray_T redir_ga; /* only valid when redir_lval is not NULL */ static char_u *redir_endp = NULL; static char_u *redir_varname = NULL; @@ -1250,6 +1252,12 @@ var_redir_stop(void) { typval_T tv; + if (EVALCMD_BUSY) + { + redir_lval = NULL; + return; + } + if (redir_lval != NULL) { /* If there was no error: assign the text to the variable. */ @@ -8556,6 +8564,7 @@ static struct fst {"empty", 1, 1, f_empty}, {"escape", 2, 2, f_escape}, {"eval", 1, 1, f_eval}, + {"evalcmd", 1, 1, f_evalcmd}, {"eventhandler", 0, 0, f_eventhandler}, {"executable", 1, 1, f_executable}, {"exepath", 1, 1, f_exepath}, @@ -11336,6 +11345,36 @@ f_eval(typval_T *argvars, typval_T *rettv) EMSG(_(e_trailing)); } +/* + * "evalcmd()" function + */ + static void +f_evalcmd(typval_T *argvars, typval_T *rettv) +{ + char_u *s; + + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + + s = get_tv_string_chk(&argvars[0]); + if (s != NULL) + { + redir_vname = TRUE; + redir_lval = (lval_T *)&redir_lval; + ga_init2(&redir_ga, (int)sizeof(char), 500); + + if (do_cmdline_cmd(s) == OK) + rettv->vval.v_string = redir_ga.ga_data; + else + vim_free(redir_ga.ga_data); + + redir_ga.ga_data = NULL; + redir_vname = FALSE; + redir_lval = NULL; + } + +} + /* * "eventhandler()" function */ diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim index e99e137640..074c2dec91 100644 --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -5,9 +5,10 @@ source test_assign.vim source test_autocmd.vim source test_cursor_func.vim source test_delete.vim +source test_evalcmd.vim source test_ex_undo.vim -source test_expr.vim source test_expand.vim +source test_expr.vim source test_expand_dllpath.vim source test_feedkeys.vim source test_fnamemodify.vim diff --git a/src/testdir/test_evalcmd.vim b/src/testdir/test_evalcmd.vim new file mode 100644 index 0000000000..e7f48bcfd3 --- /dev/null +++ b/src/testdir/test_evalcmd.vim @@ -0,0 +1,8 @@ +" test evalcmd() + +func Test_evalcmd() + call assert_equal("\nnocompatible", evalcmd('set compatible?')) + call assert_equal("\nsomething\nnice", evalcmd('echo "something\nnice"')) + call assert_fails('call evalcmd("doesnotexist")', 'E492:') +endfunc + diff --git a/src/version.c b/src/version.c index f34f7a0a79..b6b9e59eb2 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1996, /**/ 1995, /**/ From dcb170018642ec144cd87d9d9fe076575b8d1263 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 18:58:59 +0200 Subject: [PATCH 7/9] patch 7.4.1997 Problem: Cannot easily scroll the quickfix window. Solution: Add ":cbottom". --- runtime/doc/quickfix.txt | 6 ++++ src/ex_cmds.h | 3 ++ src/ex_docmd.c | 1 + src/proto/quickfix.pro | 1 + src/quickfix.c | 55 +++++++++++++++++++++++++---------- src/testdir/test_quickfix.vim | 13 +++++++++ src/version.c | 2 ++ 7 files changed, 66 insertions(+), 15 deletions(-) diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 61b8656736..9fb6b18c41 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -437,6 +437,12 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST: :lw[indow] [height] Same as ":cwindow", except use the window showing the location list for the current window. +:cbo[ttom] Put the cursor in the last line of the quickfix window + and scroll to make it visible. This is useful for + when errors are added by an asynchronous callback. + Only call it once in a while if there are many + updates to avoid a lot of redrawing. + Normally the quickfix window is at the bottom of the screen. If there are vertical splits, it's at the bottom of the rightmost column of windows. To make it always occupy the full width: > diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 5053e602e6..8c64d331ad 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -259,6 +259,9 @@ EX(CMD_catch, "catch", ex_catch, EX(CMD_cbuffer, "cbuffer", ex_cbuffer, BANG|RANGE|NOTADR|WORD1|TRLBAR, ADDR_LINES), +EX(CMD_cbottom, "cbottom", ex_cbottom, + TRLBAR, + ADDR_LINES), EX(CMD_cc, "cc", ex_cc, RANGE|NOTADR|COUNT|TRLBAR|BANG, ADDR_LINES), diff --git a/src/ex_docmd.c b/src/ex_docmd.c index ad4ba7c4e5..0be3a69102 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -129,6 +129,7 @@ static int getargopt(exarg_T *eap); # define ex_cclose ex_ni # define ex_copen ex_ni # define ex_cwindow ex_ni +# define ex_cbottom ex_ni #endif #if !defined(FEAT_QUICKFIX) || !defined(FEAT_EVAL) # define ex_cexpr ex_ni diff --git a/src/proto/quickfix.pro b/src/proto/quickfix.pro index cc60eddbd2..a2b6b76cbc 100644 --- a/src/proto/quickfix.pro +++ b/src/proto/quickfix.pro @@ -9,6 +9,7 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long void ex_cwindow(exarg_T *eap); void ex_cclose(exarg_T *eap); void ex_copen(exarg_T *eap); +void ex_cbottom(exarg_T *eap); linenr_T qf_current_entry(win_T *wp); int bt_quickfix(buf_T *buf); int bt_nofile(buf_T *buf); diff --git a/src/quickfix.c b/src/quickfix.c index 7bfd488051..45659a0242 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -2807,6 +2807,41 @@ ex_copen(exarg_T *eap) update_topline(); /* scroll to show the line */ } +/* + * Move the cursor in the quickfix window to "lnum". + */ + static void +qf_win_goto(win_T *win, linenr_T lnum) +{ + win_T *old_curwin = curwin; + + curwin = win; + curbuf = win->w_buffer; + curwin->w_cursor.lnum = lnum; + curwin->w_cursor.col = 0; +#ifdef FEAT_VIRTUALEDIT + curwin->w_cursor.coladd = 0; +#endif + curwin->w_curswant = 0; + update_topline(); /* scroll to show the line */ + redraw_later(VALID); + curwin->w_redr_status = TRUE; /* update ruler */ + curwin = old_curwin; + curbuf = curwin->w_buffer; +} + +/* + * :cbottom command. + */ + void +ex_cbottom(exarg_T *eap UNUSED) +{ + win_T *win = qf_find_win(&ql_info); + + if (win != NULL && win->w_cursor.lnum != win->w_buffer->b_ml.ml_line_count) + qf_win_goto(win, win->w_buffer->b_ml.ml_line_count); +} + /* * Return the number of the current entry (line number in the quickfix * window). @@ -2844,27 +2879,17 @@ qf_win_pos_update( && qf_index <= win->w_buffer->b_ml.ml_line_count && old_qf_index != qf_index) { - win_T *old_curwin = curwin; - - curwin = win; - curbuf = win->w_buffer; if (qf_index > old_qf_index) { - curwin->w_redraw_top = old_qf_index; - curwin->w_redraw_bot = qf_index; + win->w_redraw_top = old_qf_index; + win->w_redraw_bot = qf_index; } else { - curwin->w_redraw_top = qf_index; - curwin->w_redraw_bot = old_qf_index; + win->w_redraw_top = qf_index; + win->w_redraw_bot = old_qf_index; } - curwin->w_cursor.lnum = qf_index; - curwin->w_cursor.col = 0; - update_topline(); /* scroll to show the line */ - redraw_later(VALID); - curwin->w_redr_status = TRUE; /* update ruler */ - curwin = old_curwin; - curbuf = curwin->w_buffer; + qf_win_goto(win, qf_index); } return win != NULL; } diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index d624baf80a..01e21106ed 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -1414,3 +1414,16 @@ echo string(loc_two) call delete('Xone', 'rf') call delete('Xtwo', 'rf') endfunc + +function Test_cbottom() + call setqflist([{'filename': 'foo', 'lnum': 42}]) + copen + let wid = win_getid() + call assert_equal(1, line('.')) + wincmd w + call setqflist([{'filename': 'var', 'lnum': 24}], 'a') + cbottom + call win_gotoid(wid) + call assert_equal(2, line('.')) + cclose +endfunc diff --git a/src/version.c b/src/version.c index b6b9e59eb2..d606bab2ab 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1997, /**/ 1996, /**/ From bf2cc5f36d5ffd5de445e6970602000c7869b65a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 20:45:06 +0200 Subject: [PATCH 8/9] patch 7.4.1998 Problem: When writing buffer lines to a job there is no NL to NUL conversion. Solution: Make it work symmetrical with writing lines from a job into a buffer. --- src/channel.c | 15 ++++++++++----- src/netbeans.c | 3 ++- src/proto/channel.pro | 2 +- src/version.c | 2 ++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/channel.c b/src/channel.c index 1c93b6c50e..bb818dcd17 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1313,14 +1313,20 @@ write_buf_line(buf_T *buf, linenr_T lnum, channel_T *channel) char_u *line = ml_get_buf(buf, lnum, FALSE); int len = (int)STRLEN(line); char_u *p; + int i; /* Need to make a copy to be able to append a NL. */ if ((p = alloc(len + 2)) == NULL) return; memcpy((char *)p, (char *)line, len); + + for (i = 0; i < len; ++i) + if (p[i] == NL) + p[i] = NUL; + p[len] = NL; p[len + 1] = NUL; - channel_send(channel, PART_IN, p, "write_buf_line()"); + channel_send(channel, PART_IN, p, len + 1, "write_buf_line()"); vim_free(p); } @@ -2185,7 +2191,7 @@ channel_exe_cmd(channel_T *channel, int part, typval_T *argv) { channel_send(channel, part == PART_SOCK ? PART_SOCK : PART_IN, - json, (char *)cmd); + json, (int)STRLEN(json), (char *)cmd); vim_free(json); } } @@ -3380,9 +3386,8 @@ channel_handle_events(void) * Return FAIL or OK. */ int -channel_send(channel_T *channel, int part, char_u *buf, char *fun) +channel_send(channel_T *channel, int part, char_u *buf, int len, char *fun) { - int len = (int)STRLEN(buf); int res; sock_T fd; @@ -3470,7 +3475,7 @@ send_common( opt->jo_callback, opt->jo_partial, id); } - if (channel_send(channel, part_send, text, fun) == OK + if (channel_send(channel, part_send, text, (int)STRLEN(text), fun) == OK && opt->jo_callback == NULL) return channel; return NULL; diff --git a/src/netbeans.c b/src/netbeans.c index aadddc2c2d..e326af8f36 100644 --- a/src/netbeans.c +++ b/src/netbeans.c @@ -765,7 +765,8 @@ netbeans_end(void) nb_send(char *buf, char *fun) { if (nb_channel != NULL) - channel_send(nb_channel, PART_SOCK, (char_u *)buf, fun); + channel_send(nb_channel, PART_SOCK, (char_u *)buf, + (int)STRLEN(buf), fun); } /* diff --git a/src/proto/channel.pro b/src/proto/channel.pro index 8a059a3a9d..1acae9a105 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -34,7 +34,7 @@ int channel_read_json_block(channel_T *channel, int part, int timeout_arg, int i void common_channel_read(typval_T *argvars, typval_T *rettv, int raw); channel_T *channel_fd2channel(sock_T fd, int *partp); void channel_handle_events(void); -int channel_send(channel_T *channel, int part, char_u *buf, char *fun); +int channel_send(channel_T *channel, int part, char_u *buf, int len, char *fun); channel_T *send_common(typval_T *argvars, char_u *text, int id, int eval, jobopt_T *opt, char *fun, int *part_read); void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval); void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval); diff --git a/src/version.c b/src/version.c index d606bab2ab..368d1b8be2 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1998, /**/ 1997, /**/ From bc5d6dd1dd1dc3a06e4e655fc9479529db288365 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 7 Jul 2016 23:04:18 +0200 Subject: [PATCH 9/9] patch 7.4.1999 Problem: evalcmd() doesn't work recursively. Solution: Use redir_evalcmd instead of redir_vname. --- src/eval.c | 47 +++++++++++++++++++++++++++--------- src/globals.h | 1 + src/message.c | 14 +++++++---- src/proto/eval.pro | 3 ++- src/testdir/test_evalcmd.vim | 25 +++++++++++++++++++ src/version.c | 2 ++ 6 files changed, 75 insertions(+), 17 deletions(-) diff --git a/src/eval.c b/src/eval.c index 74ed263d51..445b6b91c4 100644 --- a/src/eval.c +++ b/src/eval.c @@ -11345,6 +11345,28 @@ f_eval(typval_T *argvars, typval_T *rettv) EMSG(_(e_trailing)); } +static garray_T redir_evalcmd_ga; + +/* + * Append "value[value_len]" to the evalcmd() output. + */ + void +evalcmd_redir_str(char_u *value, int value_len) +{ + int len; + + if (value_len == -1) + len = (int)STRLEN(value); /* Append the entire string */ + else + len = value_len; /* Append only "value_len" characters */ + if (ga_grow(&redir_evalcmd_ga, len) == OK) + { + mch_memmove((char *)redir_evalcmd_ga.ga_data + + redir_evalcmd_ga.ga_len, value, len); + redir_evalcmd_ga.ga_len += len; + } +} + /* * "evalcmd()" function */ @@ -11352,6 +11374,9 @@ f_eval(typval_T *argvars, typval_T *rettv) f_evalcmd(typval_T *argvars, typval_T *rettv) { char_u *s; + int save_msg_silent = msg_silent; + int save_redir_evalcmd = redir_evalcmd; + garray_T save_ga; rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; @@ -11359,20 +11384,20 @@ f_evalcmd(typval_T *argvars, typval_T *rettv) s = get_tv_string_chk(&argvars[0]); if (s != NULL) { - redir_vname = TRUE; - redir_lval = (lval_T *)&redir_lval; - ga_init2(&redir_ga, (int)sizeof(char), 500); + if (redir_evalcmd) + save_ga = redir_evalcmd_ga; + ga_init2(&redir_evalcmd_ga, (int)sizeof(char), 500); + redir_evalcmd = TRUE; - if (do_cmdline_cmd(s) == OK) - rettv->vval.v_string = redir_ga.ga_data; - else - vim_free(redir_ga.ga_data); + ++msg_silent; + do_cmdline_cmd(s); + rettv->vval.v_string = redir_evalcmd_ga.ga_data; + msg_silent = save_msg_silent; - redir_ga.ga_data = NULL; - redir_vname = FALSE; - redir_lval = NULL; + redir_evalcmd = save_redir_evalcmd; + if (redir_evalcmd) + redir_evalcmd_ga = save_ga; } - } /* diff --git a/src/globals.h b/src/globals.h index f14cb89a92..97711164ab 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1106,6 +1106,7 @@ EXTERN FILE *redir_fd INIT(= NULL); /* message redirection file */ #ifdef FEAT_EVAL EXTERN int redir_reg INIT(= 0); /* message redirection register */ EXTERN int redir_vname INIT(= 0); /* message redirection variable */ +EXTERN int redir_evalcmd INIT(= 0); /* evalcmd() redirection */ #endif #ifdef FEAT_LANGMAP diff --git a/src/message.c b/src/message.c index b86c886750..f793ed4143 100644 --- a/src/message.c +++ b/src/message.c @@ -3063,7 +3063,9 @@ redir_write(char_u *str, int maxlen) while (cur_col < msg_col) { #ifdef FEAT_EVAL - if (redir_reg) + if (redir_evalcmd) + evalcmd_redir_str((char_u *)" ", -1); + else if (redir_reg) write_reg_contents(redir_reg, (char_u *)" ", -1, TRUE); else if (redir_vname) var_redir_str((char_u *)" ", -1); @@ -3078,9 +3080,11 @@ redir_write(char_u *str, int maxlen) } #ifdef FEAT_EVAL - if (redir_reg) + if (redir_evalcmd) + evalcmd_redir_str(s, maxlen); + else if (redir_reg) write_reg_contents(redir_reg, s, maxlen, TRUE); - if (redir_vname) + else if (redir_vname) var_redir_str(s, maxlen); #endif @@ -3088,7 +3092,7 @@ redir_write(char_u *str, int maxlen) while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) { #ifdef FEAT_EVAL - if (!redir_reg && !redir_vname) + if (!redir_reg && !redir_vname && !redir_evalcmd) #endif if (redir_fd != NULL) putc(*s, redir_fd); @@ -3113,7 +3117,7 @@ redirecting(void) { return redir_fd != NULL || *p_vfile != NUL #ifdef FEAT_EVAL - || redir_reg || redir_vname + || redir_reg || redir_vname || redir_evalcmd #endif ; } diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 4ff063e72c..28edd782c1 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -81,12 +81,14 @@ int dict_add_list(dict_T *d, char *key, list_T *list); dictitem_T *dict_find(dict_T *d, char_u *key, int len); char_u *get_dict_string(dict_T *d, char_u *key, int save); varnumber_T get_dict_number(dict_T *d, char_u *key); +char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); int string2float(char_u *text, float_T *value); char_u *get_function_name(expand_T *xp, int idx); char_u *get_expr_name(expand_T *xp, int idx); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in); buf_T *buflist_find_by_name(char_u *name, int curtab_only); int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); +void evalcmd_redir_str(char_u *value, int value_len); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); @@ -150,5 +152,4 @@ void ex_oldfiles(exarg_T *eap); void reset_v_option_vars(void); int modify_fname(char_u *src, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen); char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags); -char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); /* vim: set ft=c : */ diff --git a/src/testdir/test_evalcmd.vim b/src/testdir/test_evalcmd.vim index e7f48bcfd3..a9bda875f9 100644 --- a/src/testdir/test_evalcmd.vim +++ b/src/testdir/test_evalcmd.vim @@ -1,8 +1,33 @@ " test evalcmd() +func NestedEval() + let nested = evalcmd('echo "nested\nlines"') + echo 'got: "' . nested . '"' +endfunc + +func NestedRedir() + redir => var + echo 'broken' + redir END +endfunc + func Test_evalcmd() call assert_equal("\nnocompatible", evalcmd('set compatible?')) call assert_equal("\nsomething\nnice", evalcmd('echo "something\nnice"')) + call assert_equal("noendofline", evalcmd('echon "noendofline"')) + call assert_equal("", evalcmd(123)) + + call assert_equal("\ngot: \"\nnested\nlines\"", evalcmd('call NestedEval()')) + redir => redired + echo 'this' + let evaled = evalcmd('echo "that"') + echo 'theend' + redir END + call assert_equal("\nthis\ntheend", redired) + call assert_equal("\nthat", evaled) + call assert_fails('call evalcmd("doesnotexist")', 'E492:') + call assert_fails('call evalcmd(3.4)', 'E806:') + call assert_fails('call evalcmd("call NestedRedir()")', 'E930:') endfunc diff --git a/src/version.c b/src/version.c index 368d1b8be2..5794e3a2fe 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1999, /**/ 1998, /**/