Merge remote-tracking branch 'vim/master'

This commit is contained in:
ichizok
2020-09-17 12:02:16 +09:00
79 changed files with 2792 additions and 1232 deletions
+37 -2
View File
@@ -2653,6 +2653,7 @@ matcharg({nr}) List arguments of |:match|
matchdelete({id} [, {win}]) Number delete match identified by {id}
matchend({expr}, {pat} [, {start} [, {count}]])
Number position where {pat} ends in {expr}
matchfuzzy({list}, {str}) List fuzzy match {str} in {list}
matchlist({expr}, {pat} [, {start} [, {count}]])
List match and submatches of {pat} in {expr}
matchstr({expr}, {pat} [, {start} [, {count}]])
@@ -7319,6 +7320,29 @@ matchend({expr}, {pat} [, {start} [, {count}]]) *matchend()*
Can also be used as a |method|: >
GetText()->matchend('word')
matchfuzzy({list}, {str}) *matchfuzzy()*
Returns a list with all the strings in {list} that fuzzy
match {str}. The strings in the returned list are sorted
based on the matching score. {str} is treated as a literal
string and regular expression matching is NOT supported.
The maximum supported {str} length is 256.
If there are no matching strings or there is an error, then an
empty list is returned. If length of {str} is greater than
256, then returns an empty list.
Example: >
:echo matchfuzzy(["clay", "crow"], "cay")
< results in ["clay"]. >
:echo getbufinfo()->map({_, v -> v.name})->matchfuzzy("ndl")
< results in a list of buffer names fuzzy matching "ndl". >
:echo v:oldfiles->matchfuzzy("test")
< results in a list of file names fuzzy matching "test". >
:let l = readfile("buffer.c")->matchfuzzy("str")
< results in a list of lines in "buffer.c" fuzzy matching "str".
matchlist({expr}, {pat} [, {start} [, {count}]]) *matchlist()*
Same as |match()|, but return a |List|. The first item in the
list is the matched string, same as what matchstr() would
@@ -12357,7 +12381,9 @@ text...
< is equivalent to: >
:let x = 1
:lockvar! x
< This is useful if you want to make sure the variable
< NOTE: in Vim9 script `:const` works differently, see
|vim9-const|
This is useful if you want to make sure the variable
is not modified. If the value is a List or Dictionary
literal then the items also cannot be changed: >
const ll = [1, 2, 3]
@@ -12397,6 +12423,8 @@ text...
[depth] is relevant when locking a |List| or
|Dictionary|. It specifies how deep the locking goes:
0 Lock the variable {name} but not its
value.
1 Lock the |List| or |Dictionary| itself,
cannot add or remove items, but can
still change their values.
@@ -12410,7 +12438,14 @@ text...
|Dictionary|, one level deeper.
The default [depth] is 2, thus when {name} is a |List|
or |Dictionary| the values cannot be changed.
*E743*
Example with [depth] 0: >
let mylist = [1, 2, 3]
lockvar 0 mylist
let mylist[0] = 77 " OK
call add(mylist, 4] " OK
let mylist = [7, 8, 9] " Error!
< *E743*
For unlimited depth use [!] and omit [depth].
However, there is a maximum depth of 100 to catch
loops.
+1
View File
@@ -603,6 +603,7 @@ String manipulation: *string-functions*
charclass() class of a character
match() position where a pattern matches in a string
matchend() position where a pattern match ends in a string
matchfuzzy() fuzzy matches a string in a list of strings
matchstr() match of a pattern in a string
matchstrpos() match and positions of a pattern in a string
matchlist() like matchstr() and also return submatches
+67 -15
View File
@@ -1,4 +1,4 @@
*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 07
*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 13
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -192,6 +192,9 @@ To intentionally avoid a variable being available later, a block can be used:
}
echo temp # Error!
Declaring a variable with a type but without an initializer will initialize to
zero, false or empty.
An existing variable cannot be assigned to with `:let`, since that implies a
declaration. Global, window, tab, buffer and Vim variables can only be used
without `:let`, because they are not really declared, they can also be deleted
@@ -210,6 +213,40 @@ at the script level. >
Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be
used to repeat a `:substitute` command.
*vim9-const*
In legacy Vim script "const list = []" would make the variable "list"
immutable and also the value. Thus you cannot add items to the list. This
differs from what many languages do. Vim9 script does it like TypeScript: only
"list" is immutable, the value can be changed.
One can use `:const!` to make both the variable and the value immutable. Use
this for composite structures that you want to make sure will not be modified.
How this works: >
vim9script
const list = [1, 2]
list = [3, 4] # Error!
list[0] = 2 # OK
const! LIST = [1, 2]
LIST = [3, 4] # Error!
LIST[0] = 2 # Error!
It is common to write constants as ALL_CAPS, but you don't have to.
The constant only applies to the value itself, not what it refers to. >
cont females = ["Mary"]
const! NAMES = [["John", "Peter"], females]
NAMES[0] = ["Jack"] # Error!
NAMES[0][0] = ["Jack"] # Error!
NAMES[1] = ["Emma"] # Error!
Names[1][0] = "Emma" # OK, now females[0] == "Emma"
Rationale: TypeScript has no way to make the value immutable. One can use
immutable types, but that quickly gets complicated for nested values. And
with a type cast the value can be made mutable again, which means there is no
guarantee the value won't change. Vim supports immutable values, in legacy
script this was done with `:lockvar`. But that is an extra statement and also
applies to nested values. Therefore the solution to use `:const!`.
*E1092*
Declaring more than one variable at a time, using the unpack notation, is
@@ -408,7 +445,7 @@ for using a list or job. This is very much like JavaScript, but there are a
few exceptions.
type TRUE when ~
bool v:true
bool v:true or 1
number non-zero
float non-zero
string non-empty
@@ -946,26 +983,41 @@ declarations: >
Expression evaluation was already close to what JavaScript and other languages
are doing. Some details are unexpected and can be fixed. For example how the
|| and && operators work. Legacy Vim script: >
let result = 44
let value = 44
...
return result || 0 # returns 1
let result = value || 0 # result == 1
Vim9 script works like JavaScript/TypeScript, keep the value: >
let result = 44
let value = 44
...
return result || 0 # returns 44
On the other hand, overloading "+" to use both for addition and string
concatenation goes against legacy Vim script and often leads to mistakes.
For that reason we will keep using ".." for string concatenation. Lua also
uses ".." this way.
let result = value || 0 # result == 44
There is no intention to completely match TypeScript syntax and semantics. We
just want to take those parts that we can use for Vim and we expect Vim users
are happy with. TypeScript is a complex language with its own advantages and
disadvantages. People used to other languages (Java, Python, etc.) will also
find things in TypeScript that they do not like or do not understand. We'll
try to avoid those things.
will be happy with. TypeScript is a complex language with its own advantages
and disadvantages. To get an idea of the disadvantages read the book:
"JavaScript: The Good Parts". Or find the article "TypeScript: the good
parts" and read the "Things to avoid" section.
People used to other languages (Java, Python, etc.) will also find things in
TypeScript that they do not like or do not understand. We'll try to avoid
those things.
Specific items from TypeScript we avoid:
- Overloading "+", using it both for addition and string concatenation. This
goes against legacy Vim script and often leads to mistakes. For that reason
we will keep using ".." for string concatenation. Lua also uses ".." this
way. And it allows for conversion to string for more values.
- TypeScript can use an expression like "99 || 'yes'" in a condition, but
cannot assign the value to a boolean. That is inconsistent and can be
annoying. Vim recognizes an expression with && or || and allows using the
result as a bool.
- TypeScript considers an empty string as Falsy, but an empty list or dict as
Truthy. That is inconsistent. In Vim an empty list and dict are also
Falsy.
- TypeScript has various "Readonly" types, which have limited usefulness,
since a type cast can remove the immutable nature. Vim locks the value,
which is more flexible, but is only checked at runtime.
Import and Export ~
+476 -476
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -693,7 +693,7 @@ changed_bytes(linenr_T lnum, colnr_T col)
* Like changed_bytes() but also adjust text properties for "added" bytes.
* When "added" is negative text was deleted.
*/
static void
void
inserted_bytes(linenr_T lnum, colnr_T col, int added UNUSED)
{
#ifdef FEAT_PROP_POPUP
+11 -11
View File
@@ -996,8 +996,8 @@ channel_open(
CLEAR_FIELD(hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
# ifdef AI_ADDRCONFIG
hints.ai_flags = AI_ADDRCONFIG;
# if defined(AI_ADDRCONFIG) && defined(AI_V4MAPPED)
hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
# endif
// Set port number manually in order to prevent name resolution services
// from being invoked in the environment where AI_NUMERICSERV is not
@@ -2645,9 +2645,7 @@ invoke_one_time_callback(
static void
append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part)
{
bufref_T save_curbuf = {NULL, 0, 0};
win_T *save_curwin = NULL;
tabpage_T *save_curtab = NULL;
aco_save_T aco;
linenr_T lnum = buffer->b_ml.ml_line_count;
int save_write_to = buffer->b_write_to_channel;
chanpart_T *ch_part = &channel->ch_part[part];
@@ -2673,12 +2671,13 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part)
}
// Append to the buffer
ch_log(channel, "appending line %d to buffer", (int)lnum + 1 - empty);
ch_log(channel, "appending line %d to buffer %s",
(int)lnum + 1 - empty, buffer->b_fname);
buffer->b_p_ma = TRUE;
// Save curbuf/curwin/curtab and make "buffer" the current buffer.
switch_to_win_for_buf(buffer, &save_curwin, &save_curtab, &save_curbuf);
// set curbuf to be our buf, temporarily
aucmd_prepbuf(&aco, buffer);
u_sync(TRUE);
// ignore undo failure, undo is not very useful here
@@ -2694,8 +2693,8 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part)
ml_append(lnum, msg, 0, FALSE);
appended_lines_mark(lnum, 1L);
// Restore curbuf/curwin/curtab
restore_win_for_buf(save_curwin, save_curtab, &save_curbuf);
// reset notion of buffer
aucmd_restbuf(&aco);
if (ch_part->ch_nomodifiable)
buffer->b_p_ma = FALSE;
@@ -2719,9 +2718,10 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part)
// down. If the topline is outdated update it now.
if (move_cursor || wp->w_topline > buffer->b_ml.ml_line_count)
{
win_T *save_curwin = curwin;
if (move_cursor)
++wp->w_cursor.lnum;
save_curwin = curwin;
curwin = wp;
curbuf = curwin->w_buffer;
scroll_cursor_bot(0, FALSE);
+1 -1
View File
@@ -992,7 +992,7 @@ set_one_cmd_context(
}
// 3. Skip over the range to find the command.
cmd = skip_range(cmd, &xp->xp_context);
cmd = skip_range(cmd, TRUE, &xp->xp_context);
xp->xp_pattern = cmd;
if (*cmd == NUL)
return NULL;
+3 -3
View File
@@ -105,7 +105,7 @@ do_debug(char_u *cmd)
vim_free(debug_newval);
debug_newval = NULL;
}
sname = estack_sfile(FALSE);
sname = estack_sfile(ESTACK_NONE);
if (sname != NULL)
msg((char *)sname);
vim_free(sname);
@@ -344,7 +344,7 @@ do_checkbacktracelevel(void)
}
else
{
char_u *sname = estack_sfile(FALSE);
char_u *sname = estack_sfile(ESTACK_NONE);
int max = get_maxbacktrace_level(sname);
if (debug_backtrace_level > max)
@@ -365,7 +365,7 @@ do_showbacktrace(char_u *cmd)
int i = 0;
int max;
sname = estack_sfile(FALSE);
sname = estack_sfile(ESTACK_NONE);
max = get_maxbacktrace_level(sname);
if (sname != NULL)
{
+2 -2
View File
@@ -1009,7 +1009,7 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action)
}
else if (*action == 'f' && HI2DI(hi2) != di1)
{
if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
|| var_check_ro(di1->di_flags, arg_errmsg, TRUE))
break;
clear_tv(&di1->di_tv);
@@ -1227,7 +1227,7 @@ dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
if (argvars[2].v_type != VAR_UNKNOWN)
semsg(_(e_toomanyarg), "remove()");
else if ((d = argvars[0].vval.v_dict) != NULL
&& !var_check_lock(d->dv_lock, arg_errmsg, TRUE))
&& !value_check_lock(d->dv_lock, arg_errmsg, TRUE))
{
key = tv_get_string_chk(&argvars[1]);
if (key != NULL)
+5 -1
View File
@@ -2779,8 +2779,12 @@ win_line(
// highlight the cursor position itself.
// Also highlight the 'colorcolumn' if it is different than
// 'cursorcolumn'
// Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak'
// options are set
vcol_save_attr = -1;
if (draw_state == WL_LINE && !lnum_in_visual_area
if ((draw_state == WL_LINE ||
draw_state == WL_BRI ||
draw_state == WL_SBR) && !lnum_in_visual_area
&& search_attr == 0 && area_attr == 0)
{
if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol
+49 -37
View File
@@ -22,27 +22,27 @@ EXTERN char e_invalid_command[]
EXTERN char e_invalid_command_str[]
INIT(= N_("E476: Invalid command: %s"));
EXTERN char e_cannot_slice_dictionary[]
INIT(= N_("E719: cannot slice a Dictionary"));
INIT(= N_("E719: Cannot slice a Dictionary"));
EXTERN char e_assert_fails_second_arg[]
INIT(= N_("E856: assert_fails() second argument must be a string or a list with one or two strings"));
INIT(= N_("E856: \"assert_fails()\" second argument must be a string or a list with one or two strings"));
EXTERN char e_cannot_index_special_variable[]
INIT(= N_("E909: Cannot index a special variable"));
EXTERN char e_missing_let_str[]
INIT(= N_("E1100: Missing :let: %s"));
EXTERN char e_variable_not_found_str[]
INIT(= N_("E1001: variable not found: %s"));
INIT(= N_("E1001: Variable not found: %s"));
EXTERN char e_syntax_error_at_str[]
INIT(= N_("E1002: Syntax error at %s"));
EXTERN char e_missing_return_value[]
INIT(= N_("E1003: Missing return value"));
EXTERN char e_white_space_required_before_and_after_str[]
INIT(= N_("E1004: white space required before and after '%s'"));
INIT(= N_("E1004: White space required before and after '%s'"));
EXTERN char e_too_many_argument_types[]
INIT(= N_("E1005: Too many argument types"));
EXTERN char e_str_is_used_as_argument[]
INIT(= N_("E1006: %s is used as an argument"));
EXTERN char e_mandatory_argument_after_optional_argument[]
INIT(= N_("E1007: mandatory argument after optional argument"));
INIT(= N_("E1007: Mandatory argument after optional argument"));
EXTERN char e_missing_type[]
INIT(= N_("E1008: Missing <type>"));
EXTERN char e_missing_gt_after_type[]
@@ -50,11 +50,11 @@ EXTERN char e_missing_gt_after_type[]
EXTERN char e_type_not_recognized_str[]
INIT(= N_("E1010: Type not recognized: %s"));
EXTERN char e_name_too_long_str[]
INIT(= N_("E1011: name too long: %s"));
INIT(= N_("E1011: Name too long: %s"));
EXTERN char e_type_mismatch_expected_str_but_got_str[]
INIT(= N_("E1012: type mismatch, expected %s but got %s"));
INIT(= N_("E1012: Type mismatch; expected %s but got %s"));
EXTERN char e_argument_nr_type_mismatch_expected_str_but_got_str[]
INIT(= N_("E1013: argument %d: type mismatch, expected %s but got %s"));
INIT(= N_("E1013: Argument %d: type mismatch, expected %s but got %s"));
EXTERN char e_invalid_key_str[]
INIT(= N_("E1014: Invalid key: %s"));
EXTERN char e_name_expected[]
@@ -70,23 +70,23 @@ EXTERN char e_cannot_assign_to_constant[]
EXTERN char e_can_only_concatenate_to_string[]
INIT(= N_("E1019: Can only concatenate to string"));
EXTERN char e_cannot_use_operator_on_new_variable[]
INIT(= N_("E1020: cannot use an operator on a new variable: %s"));
INIT(= N_("E1020: Cannot use an operator on a new variable: %s"));
EXTERN char e_const_requires_a_value[]
INIT(= N_("E1021: const requires a value"));
INIT(= N_("E1021: Const requires a value"));
EXTERN char e_type_or_initialization_required[]
INIT(= N_("E1022: type or initialization required"));
INIT(= N_("E1022: Type or initialization required"));
EXTERN char e_using_number_as_bool_nr[]
INIT(= N_("E1023: Using a Number as a Bool: %d"));
EXTERN char e_using_number_as_string[]
INIT(= N_("E1024: Using a Number as a String"));
EXTERN char e_using_rcurly_outside_if_block_scope[]
INIT(= N_("E1025: using } outside of a block scope"));
INIT(= N_("E1025: Using } outside of a block scope"));
EXTERN char e_missing_rcurly[]
INIT(= N_("E1026: Missing }"));
EXTERN char e_missing_return_statement[]
INIT(= N_("E1027: Missing return statement"));
EXTERN char e_compile_def_function_failed[]
INIT(= N_("E1028: compile_def_function failed"));
INIT(= N_("E1028: Compiling :def function failed"));
EXTERN char e_expected_str_but_got_str[]
INIT(= N_("E1029: Expected %s but got %s"));
EXTERN char e_using_string_as_number[]
@@ -94,9 +94,9 @@ EXTERN char e_using_string_as_number[]
EXTERN char e_cannot_use_void_value[]
INIT(= N_("E1031: Cannot use void value"));
EXTERN char e_missing_catch_or_finally[]
INIT(= N_("E1032: missing :catch or :finally"));
INIT(= N_("E1032: Missing :catch or :finally"));
EXTERN char e_catch_unreachable_after_catch_all[]
INIT(= N_("E1033: catch unreachable after catch-all"));
INIT(= N_("E1033: Catch unreachable after catch-all"));
EXTERN char e_cannot_use_reserved_name[]
INIT(= N_("E1034: Cannot use reserved name %s"));
EXTERN char e_percent_requires_number_arguments[]
@@ -106,25 +106,25 @@ EXTERN char e_char_requires_number_or_float_arguments[]
EXTERN char e_cannot_use_str_with_str[]
INIT(= N_("E1037: Cannot use \"%s\" with %s"));
EXTERN char e_vim9script_can_only_be_used_in_script[]
INIT(= N_("E1038: vim9script can only be used in a script"));
INIT(= N_("E1038: \"vim9script\" can only be used in a script"));
EXTERN char e_vim9script_must_be_first_command_in_script[]
INIT(= N_("E1039: vim9script must be the first command in a script"));
INIT(= N_("E1039: \"vim9script\" must be the first command in a script"));
EXTERN char e_cannot_use_scriptversion_after_vim9script[]
INIT(= N_("E1040: Cannot use :scriptversion after :vim9script"));
EXTERN char e_redefining_script_item_str[]
INIT(= N_("E1041: Redefining script item %s"));
EXTERN char e_export_can_only_be_used_in_vim9script[]
INIT(= N_("E1042: export can only be used in vim9script"));
INIT(= N_("E1042: Export can only be used in vim9script"));
EXTERN char e_invalid_command_after_export[]
INIT(= N_("E1043: Invalid command after :export"));
EXTERN char e_export_with_invalid_argument[]
INIT(= N_("E1044: export with invalid argument"));
INIT(= N_("E1044: Export with invalid argument"));
EXTERN char e_missing_as_after_star[]
INIT(= N_("E1045: Missing \"as\" after *"));
EXTERN char e_missing_comma_in_import[]
INIT(= N_("E1046: Missing comma in import"));
EXTERN char e_syntax_error_in_import[]
INIT(= N_("E1047: syntax error in import"));
INIT(= N_("E1047: Syntax error in import"));
EXTERN char e_item_not_found_in_script_str[]
INIT(= N_("E1048: Item not found in script: %s"));
EXTERN char e_item_not_exported_in_script_str[]
@@ -132,7 +132,7 @@ EXTERN char e_item_not_exported_in_script_str[]
EXTERN char e_colon_required_before_a_range[]
INIT(= N_("E1050: Colon required before a range"));
EXTERN char e_wrong_argument_type_for_plus[]
INIT(= N_("E1051: wrong argument type for +"));
INIT(= N_("E1051: Wrong argument type for +"));
EXTERN char e_cannot_declare_an_option[]
INIT(= N_("E1052: Cannot declare an option: %s"));
EXTERN char e_could_not_import_str[]
@@ -142,21 +142,21 @@ EXTERN char e_variable_already_declared_in_script[]
EXTERN char e_missing_name_after_dots[]
INIT(= N_("E1055: Missing name after ..."));
EXTERN char e_expected_type_str[]
INIT(= N_("E1056: expected a type: %s"));
INIT(= N_("E1056: Expected a type: %s"));
EXTERN char e_missing_enddef[]
INIT(= N_("E1057: Missing :enddef"));
EXTERN char e_function_nesting_too_deep[]
INIT(= N_("E1058: function nesting too deep"));
INIT(= N_("E1058: Function nesting too deep"));
EXTERN char e_no_white_space_allowed_before_colon_str[]
INIT(= N_("E1059: No white space allowed before colon: %s"));
EXTERN char e_expected_dot_after_name_str[]
INIT(= N_("E1060: expected dot after name: %s"));
INIT(= N_("E1060: Expected dot after name: %s"));
EXTERN char e_cannot_find_function_str[]
INIT(= N_("E1061: Cannot find function %s"));
EXTERN char e_cannot_index_number[]
INIT(= N_("E1062: Cannot index a Number"));
EXTERN char e_type_mismatch_for_v_variable[]
INIT(= N_("E1063: type mismatch for v: variable"));
INIT(= N_("E1063: Type mismatch for v: variable"));
// E1064 unused
// E1065 unused
EXTERN char e_cannot_declare_a_register_str[]
@@ -166,7 +166,7 @@ EXTERN char e_separator_mismatch_str[]
EXTERN char e_no_white_space_allowed_before_str[]
INIT(= N_("E1068: No white space allowed before '%s'"));
EXTERN char e_white_space_required_after_str[]
INIT(= N_("E1069: white space required after '%s'"));
INIT(= N_("E1069: White space required after '%s'"));
EXTERN char e_missing_from[]
INIT(= N_("E1070: Missing \"from\""));
EXTERN char e_invalid_string_after_from[]
@@ -174,9 +174,9 @@ EXTERN char e_invalid_string_after_from[]
EXTERN char e_cannot_compare_str_with_str[]
INIT(= N_("E1072: Cannot compare %s with %s"));
EXTERN char e_name_already_defined_str[]
INIT(= N_("E1073: name already defined: %s"));
INIT(= N_("E1073: Name already defined: %s"));
EXTERN char e_no_white_space_allowed_after_dot[]
INIT(= N_("E1074: no white space allowed after dot"));
INIT(= N_("E1074: No white space allowed after dot"));
EXTERN char e_namespace_not_supported_str[]
INIT(= N_("E1075: Namespace not supported: %s"));
EXTERN char e_this_vim_is_not_compiled_with_float_support[]
@@ -191,7 +191,7 @@ EXTERN char e_cannot_unlet_str[]
EXTERN char e_cannot_use_namespaced_variable[]
INIT(= N_("E1082: Cannot use a namespaced variable: %s"));
EXTERN char e_missing_backtick[]
INIT(= N_("E1083: missing backtick"));
INIT(= N_("E1083: Missing backtick"));
EXTERN char e_cannot_delete_vim9_script_function_str[]
INIT(= N_("E1084: Cannot delete Vim9 script function %s"));
EXTERN char e_not_callable_type_str[]
@@ -199,10 +199,10 @@ EXTERN char e_not_callable_type_str[]
EXTERN char e_cannot_use_function_inside_def[]
INIT(= N_("E1086: Cannot use :function inside :def"));
EXTERN char e_cannot_use_index_when_declaring_variable[]
INIT(= N_("E1087: cannot use an index when declaring a variable"));
INIT(= N_("E1087: Cannot use an index when declaring a variable"));
// E1088 unused
EXTERN char e_unknown_variable_str[]
INIT(= N_("E1089: unknown variable: %s"));
INIT(= N_("E1089: Unknown variable: %s"));
EXTERN char e_cannot_assign_to_argument[]
INIT(= N_("E1090: Cannot assign to argument %s"));
EXTERN char e_function_is_not_compiled_str[]
@@ -212,20 +212,20 @@ EXTERN char e_cannot_use_list_for_declaration[]
EXTERN char e_expected_nr_items_but_got_nr[]
INIT(= N_("E1093: Expected %d items but got %d"));
EXTERN char e_import_can_only_be_used_in_script[]
INIT(= N_("E1094: import can only be used in a script"));
INIT(= N_("E1094: Import can only be used in a script"));
EXTERN char e_unreachable_code_after_return[]
INIT(= N_("E1095: Unreachable code after :return"));
EXTERN char e_returning_value_in_function_without_return_type[]
INIT(= N_("E1096: Returning a value in a function without a return type"));
EXTERN char e_line_incomplete[]
INIT(= N_("E1097: line incomplete"));
INIT(= N_("E1097: Line incomplete"));
// E1098 unused
EXTERN char e_unknown_error_while_executing_str[]
INIT(= N_("E1099: Unknown error while executing %s"));
EXTERN char e_cannot_declare_script_variable_in_function[]
INIT(= N_("E1101: Cannot declare a script variable in a function: %s"));
EXTERN char e_lambda_function_not_found_str[]
INIT(= N_("E1102: lambda function not found: %s"));
INIT(= N_("E1102: Lambda function not found: %s"));
EXTERN char e_dictionary_not_set[]
INIT(= N_("E1103: Dictionary not set"));
EXTERN char e_missing_gt[]
@@ -233,7 +233,7 @@ EXTERN char e_missing_gt[]
EXTERN char e_cannot_convert_str_to_string[]
INIT(= N_("E1105: Cannot convert %s to string"));
EXTERN char e_one_argument_too_many[]
INIT(= N_("E1106: one argument too many"));
INIT(= N_("E1106: One argument too many"));
EXTERN char e_nr_arguments_too_many[]
INIT(= N_("E1106: %d arguments too many"));
EXTERN char e_string_list_dict_or_blob_required[]
@@ -253,7 +253,19 @@ EXTERN char e_overlapping_ranges_for_nr[]
EXTERN char e_only_values_of_0x100_and_higher_supported[]
INIT(= N_("E1114: Only values of 0x100 and higher supported"));
EXTERN char e_assert_fails_fourth_argument[]
INIT(= N_("E1115: assert_fails() fourth argument must be a number"));
INIT(= N_("E1115: \"assert_fails()\" fourth argument must be a number"));
EXTERN char e_assert_fails_fifth_argument[]
INIT(= N_("E1116: assert_fails() fifth argument must be a string"));
INIT(= N_("E1116: \"assert_fails()\" fifth argument must be a string"));
EXTERN char e_cannot_use_bang_with_nested_def[]
INIT(= N_("E1117: Cannot use ! with nested :def"));
EXTERN char e_cannot_change_list[]
INIT(= N_("E1118: Cannot change list"));
EXTERN char e_cannot_change_list_item[]
INIT(= N_("E1119: Cannot change list item"));
EXTERN char e_cannot_change_dict[]
INIT(= N_("E1120: Cannot change dict"));
EXTERN char e_cannot_change_dict_item[]
INIT(= N_("E1121: Cannot change dict item"));
EXTERN char e_variable_is_locked_str[]
INIT(= N_("E1122: Variable is locked: %s"));
#endif
+9 -6
View File
@@ -1055,7 +1055,8 @@ get_lval(
}
// existing variable, need to check if it can be changed
else if ((flags & GLV_READ_ONLY) == 0
&& var_check_ro(lp->ll_di->di_flags, name, FALSE))
&& (var_check_ro(lp->ll_di->di_flags, name, FALSE)
|| var_check_lock(lp->ll_di->di_flags, name, FALSE)))
{
clear_tv(&var1);
return NULL;
@@ -1200,7 +1201,7 @@ set_var_lval(
char_u *endp,
typval_T *rettv,
int copy,
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
char_u *op)
{
int cc;
@@ -1220,7 +1221,7 @@ set_var_lval(
semsg(_(e_letwrong), op);
return;
}
if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, FALSE))
if (value_check_lock(lp->ll_blob->bv_lock, lp->ll_name, FALSE))
return;
if (lp->ll_range && rettv->v_type == VAR_BLOB)
@@ -1297,7 +1298,7 @@ set_var_lval(
}
*endp = cc;
}
else if (var_check_lock(lp->ll_newkey == NULL
else if (value_check_lock(lp->ll_newkey == NULL
? lp->ll_tv->v_lock
: lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name, FALSE))
;
@@ -1317,7 +1318,7 @@ set_var_lval(
*/
for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; )
{
if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
return;
ri = ri->li_next;
if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1))
@@ -2103,6 +2104,8 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
char_u *p;
int getnext;
CLEAR_POINTER(rettv);
/*
* Get the first variable.
*/
@@ -3586,7 +3589,7 @@ eval_index(
;
if (keylen == 0)
return FAIL;
*arg = skipwhite(key + keylen);
*arg = key + keylen;
}
else
{
+10 -6
View File
@@ -386,11 +386,14 @@ ret_argv(int argcount, type_T **argtypes UNUSED)
static type_T *
ret_remove(int argcount UNUSED, type_T **argtypes)
{
if (argtypes[0]->tt_type == VAR_LIST
|| argtypes[0]->tt_type == VAR_DICT)
return argtypes[0]->tt_member;
if (argtypes[0]->tt_type == VAR_BLOB)
return &t_number;
if (argtypes != NULL)
{
if (argtypes[0]->tt_type == VAR_LIST
|| argtypes[0]->tt_type == VAR_DICT)
return argtypes[0]->tt_member;
if (argtypes[0]->tt_type == VAR_BLOB)
return &t_number;
}
return &t_any;
}
@@ -750,6 +753,7 @@ static funcentry_T global_functions[] =
{"matcharg", 1, 1, FEARG_1, ret_list_string, f_matcharg},
{"matchdelete", 1, 2, FEARG_1, ret_number, f_matchdelete},
{"matchend", 2, 4, FEARG_1, ret_number, f_matchend},
{"matchfuzzy", 2, 2, FEARG_1, ret_list_string, f_matchfuzzy},
{"matchlist", 2, 4, FEARG_1, ret_list_string, f_matchlist},
{"matchstr", 2, 4, FEARG_1, ret_string, f_matchstr},
{"matchstrpos", 2, 4, FEARG_1, ret_list_any, f_matchstrpos},
@@ -2914,7 +2918,7 @@ ret_f_function(int argcount, type_T **argtypes)
{
if (argcount == 1 && argtypes[0]->tt_type == VAR_STRING)
return &t_func_any;
return &t_func_void;
return &t_func_unknown;
}
/*
+66 -36
View File
@@ -175,7 +175,6 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op);
static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
static void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
static void delete_var(hashtab_T *ht, hashitem_T *hi);
static void list_one_var(dictitem_T *v, char *prefix, int *first);
static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first);
@@ -711,6 +710,8 @@ ex_let(exarg_T *eap)
// detect Vim9 assignment without ":let" or ":const"
if (eap->arg == eap->cmd)
flags |= LET_NO_COMMAND;
if (eap->forceit)
flags |= LET_FORCEIT;
argend = skip_var_list(arg, TRUE, &var_count, &semicolon, FALSE);
if (argend == NULL)
@@ -861,7 +862,7 @@ ex_let_vars(
int copy, // copy values from "tv", don't move
int semicolon, // from skip_var_list()
int var_count, // from skip_var_list()
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
char_u *op)
{
char_u *arg = arg_start;
@@ -1216,7 +1217,7 @@ ex_let_one(
char_u *arg, // points to variable name
typval_T *tv, // value to assign to variable
int copy, // copy value from "tv"
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
char_u *endchars, // valid chars after variable name or NULL
char_u *op) // "+", "-", "." or NULL
{
@@ -1561,9 +1562,9 @@ do_unlet_var(
*name_end = cc;
}
else if ((lp->ll_list != NULL
&& var_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE))
&& value_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE))
|| (lp->ll_dict != NULL
&& var_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE)))
&& value_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE)))
return FAIL;
else if (lp->ll_range)
{
@@ -1574,7 +1575,7 @@ do_unlet_var(
while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1))
{
li = ll_li->li_next;
if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
return FAIL;
ll_li = li;
++ll_n1;
@@ -1647,7 +1648,7 @@ do_unlet(char_u *name, int forceit)
di = HI2DI(hi);
if (var_check_fixed(di->di_flags, name, FALSE)
|| var_check_ro(di->di_flags, name, FALSE)
|| var_check_lock(d->dv_lock, name, FALSE))
|| value_check_lock(d->dv_lock, name, FALSE))
return FAIL;
delete_var(ht, hi);
@@ -1678,9 +1679,6 @@ do_lock_var(
int cc;
dictitem_T *di;
if (deep == 0) // nothing to do
return OK;
if (lp->ll_tv == NULL)
{
cc = *name_end;
@@ -1711,11 +1709,16 @@ do_lock_var(
di->di_flags |= DI_FLAGS_LOCK;
else
di->di_flags &= ~DI_FLAGS_LOCK;
item_lock(&di->di_tv, deep, lock, FALSE);
if (deep != 0)
item_lock(&di->di_tv, deep, lock, FALSE);
}
}
*name_end = cc;
}
else if (deep == 0)
{
// nothing to do
}
else if (lp->ll_range)
{
listitem_T *li = lp->ll_li;
@@ -1743,7 +1746,7 @@ do_lock_var(
* When "check_refcount" is TRUE do not lock a list or dict with a reference
* count larger than 1.
*/
static void
void
item_lock(typval_T *tv, int deep, int lock, int check_refcount)
{
static int recurse = 0;
@@ -2939,7 +2942,7 @@ set_var_const(
type_T *type,
typval_T *tv_arg,
int copy, // make copy of value in "tv"
int flags) // LET_IS_CONST and/or LET_NO_COMMAND
int flags) // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
{
typval_T *tv = tv_arg;
typval_T bool_tv;
@@ -2947,22 +2950,23 @@ set_var_const(
char_u *varname;
hashtab_T *ht;
int is_script_local;
int vim9script = in_vim9script();
ht = find_var_ht(name, &varname);
if (ht == NULL || *varname == NUL)
{
semsg(_(e_illvar), name);
return;
goto failed;
}
is_script_local = ht == get_script_local_ht();
if (in_vim9script()
if (vim9script
&& !is_script_local
&& (flags & LET_NO_COMMAND) == 0
&& name[1] == ':')
{
vim9_declare_error(name);
return;
goto failed;
}
di = find_var_in_ht(ht, 0, varname, TRUE);
@@ -2973,7 +2977,7 @@ set_var_const(
if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
&& var_wrong_func_name(name, di == NULL))
return;
goto failed;
if (need_convert_to_bool(type, tv))
{
@@ -2991,25 +2995,30 @@ set_var_const(
if (flags & LET_IS_CONST)
{
emsg(_(e_cannot_mod));
return;
goto failed;
}
if (is_script_local && in_vim9script())
if (is_script_local && vim9script)
{
if ((flags & LET_NO_COMMAND) == 0)
{
semsg(_(e_redefining_script_item_str), name);
return;
goto failed;
}
// check the type and adjust to bool if needed
if (check_script_var_type(&di->di_tv, tv, name) == FAIL)
return;
goto failed;
}
// Check in this order for backwards compatibility:
// - Whether the variable is read-only
// - Whether the variable value is locked
// - Whether the variable is locked
if (var_check_ro(di->di_flags, name, FALSE)
|| var_check_lock(di->di_tv.v_lock, name, FALSE))
return;
|| value_check_lock(di->di_tv.v_lock, name, FALSE)
|| var_check_lock(di->di_flags, name, FALSE))
goto failed;
}
else
// can only redefine once
@@ -3039,7 +3048,7 @@ set_var_const(
di->di_tv.vval.v_string = tv->vval.v_string;
tv->vval.v_string = NULL;
}
return;
goto failed;
}
else if (di->di_tv.v_type == VAR_NUMBER)
{
@@ -3053,12 +3062,12 @@ set_var_const(
redraw_all_later(SOME_VALID);
}
#endif
return;
goto failed;
}
else if (di->di_tv.v_type != tv->v_type)
{
semsg(_("E963: setting %s to value with wrong type"), name);
return;
goto failed;
}
}
@@ -3070,27 +3079,27 @@ set_var_const(
if (ht == &vimvarht || ht == get_funccal_args_ht())
{
semsg(_(e_illvar), name);
return;
goto failed;
}
// Make sure the variable name is valid.
if (!valid_varname(varname))
return;
goto failed;
di = alloc(sizeof(dictitem_T) + STRLEN(varname));
if (di == NULL)
return;
goto failed;
STRCPY(di->di_key, varname);
if (hash_add(ht, DI2HIKEY(di)) == FAIL)
{
vim_free(di);
return;
goto failed;
}
di->di_flags = DI_FLAGS_ALLOC;
if (flags & LET_IS_CONST)
di->di_flags |= DI_FLAGS_LOCK;
if (is_script_local && in_vim9script())
if (is_script_local && vim9script)
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
@@ -3125,11 +3134,16 @@ set_var_const(
init_tv(tv);
}
if (flags & LET_IS_CONST)
// ":const var = val" locks the value; in Vim9 script only with ":const!"
if ((flags & LET_IS_CONST) && (!vim9script || (flags & LET_FORCEIT)))
// Like :lockvar! name: lock the value and what it contains, but only
// if the reference count is up to one. That locks only literal
// values.
item_lock(&di->di_tv, DICT_MAXNEST, TRUE, TRUE);
return;
failed:
if (!copy)
clear_tv(tv_arg);
}
/*
@@ -3152,6 +3166,22 @@ var_check_ro(int flags, char_u *name, int use_gettext)
return FALSE;
}
/*
* Return TRUE if di_flags "flags" indicates variable "name" is locked.
* Also give an error message.
*/
int
var_check_lock(int flags, char_u *name, int use_gettext)
{
if (flags & DI_FLAGS_LOCK)
{
semsg(_(e_variable_is_locked_str),
use_gettext ? (char_u *)_(name) : name);
return TRUE;
}
return FALSE;
}
/*
* Return TRUE if di_flags "flags" indicates variable "name" is fixed.
* Also give an error message.
@@ -3199,12 +3229,12 @@ var_wrong_func_name(
}
/*
* Return TRUE if "flags" indicates variable "name" is locked (immutable).
* Also give an error message, using "name" or _("name") when use_gettext is
* TRUE.
* Return TRUE if "flags" indicates variable "name" has a locked (immutable)
* value. Also give an error message, using "name" or _("name") when
* "use_gettext" is TRUE.
*/
int
var_check_lock(int lock, char_u *name, int use_gettext)
value_check_lock(int lock, char_u *name, int use_gettext)
{
if (lock & VAR_LOCKED)
{
+1 -1
View File
@@ -398,7 +398,7 @@ EXCMD(CMD_confirm, "confirm", ex_wrongmodifier,
EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_const, "const", ex_let,
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
EX_EXTRA|EX_BANG|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_copen, "copen", ex_copen,
EX_RANGE|EX_COUNT|EX_TRLBAR,
+32 -13
View File
@@ -1785,9 +1785,7 @@ do_one_cmd(
may_have_range = !vim9script || starts_with_colon;
if (may_have_range)
#endif
ea.cmd = skip_range(ea.cmd, NULL);
if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
ea.cmd = skipwhite(ea.cmd + 1);
ea.cmd = skip_range(ea.cmd, TRUE, NULL);
#ifdef FEAT_EVAL
if (vim9script && !starts_with_colon)
@@ -2689,7 +2687,7 @@ parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only)
return FAIL;
}
p = skip_range(eap->cmd, NULL);
p = skip_range(eap->cmd, TRUE, NULL);
switch (*p)
{
// When adding an entry, also modify cmd_exists().
@@ -3232,19 +3230,33 @@ find_ex_command(
// "g:varname" is an expression.
|| eap->cmd[1] == ':'
)
: (
// "varname[]" is an expression.
*p == '['
// "varname->func()" is an expression.
|| (*p == '-' && p[1] == '>')
// "varname.expr" is an expression.
|| (*p == '.' && ASCII_ISALPHA(p[1]))
)))
: (*p == '-' && p[1] == '>')))
{
eap->cmdidx = CMD_eval;
return eap->cmd;
}
if (p != eap->cmd && (
// "varname[]" is an expression.
*p == '['
// "varname.key" is an expression.
|| (*p == '.' && ASCII_ISALPHA(p[1]))))
{
char_u *after = p;
// When followed by "=" or "+=" then it is an assignment.
++emsg_silent;
if (skip_expr(&after) == OK
&& (*after == '='
|| (*after != NUL && after[1] == '=')))
eap->cmdidx = CMD_let;
else
eap->cmdidx = CMD_eval;
--emsg_silent;
return eap->cmd;
}
// "[...]->Method()" is a list expression, but "[a, b] = Func()" is
// an assignment.
// If there is no line break inside the "[...]" then "p" is
@@ -3540,7 +3552,8 @@ excmd_get_argt(cmdidx_T idx)
char_u *
skip_range(
char_u *cmd,
int *ctx) // pointer to xp_context or NULL
int skip_star, // skip "*" used for Visual range
int *ctx) // pointer to xp_context or NULL
{
unsigned delim;
@@ -3575,6 +3588,10 @@ skip_range(
while (*cmd == ':')
cmd = skipwhite(cmd + 1);
// Skip "*" used for Visual range.
if (skip_star && *cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
cmd = skipwhite(cmd + 1);
return cmd;
}
@@ -8405,6 +8422,7 @@ find_cmdline_var(char_u *src, int *usedlen)
* '<cexpr>' to C-expression under the cursor
* '<cfile>' to path name under the cursor
* '<sfile>' to sourced file name
* '<stack>' to call stack
* '<slnum>' to sourced file line number
* '<afile>' to file name for autocommand
* '<abuf>' to buffer number for autocommand
@@ -8622,7 +8640,8 @@ eval_vars(
case SPEC_SFILE: // file name for ":so" command
case SPEC_STACK: // call stack
result = estack_sfile(spec_idx == SPEC_SFILE);
result = estack_sfile(spec_idx == SPEC_SFILE
? ESTACK_SFILE : ESTACK_STACK);
if (result == NULL)
{
*errormsg = spec_idx == SPEC_SFILE
+2 -2
View File
@@ -290,7 +290,7 @@ cause_errthrow(
// Get the source name and lnum now, it may change before
// reaching do_errthrow().
elem->sfile = estack_sfile(FALSE);
elem->sfile = estack_sfile(ESTACK_NONE);
elem->slnum = SOURCING_LNUM;
}
}
@@ -549,7 +549,7 @@ throw_exception(void *value, except_type_T type, char_u *cmdname)
}
else
{
excp->throw_name = estack_sfile(FALSE);
excp->throw_name = estack_sfile(ESTACK_NONE);
if (excp->throw_name == NULL)
excp->throw_name = vim_strsave((char_u *)"");
if (excp->throw_name == NULL)
+7 -3
View File
@@ -187,8 +187,12 @@ set_search_match(pos_T *t)
* May change the last search pattern.
*/
static int
do_incsearch_highlighting(int firstc, int *search_delim, incsearch_state_T *is_state,
int *skiplen, int *patlen)
do_incsearch_highlighting(
int firstc,
int *search_delim,
incsearch_state_T *is_state,
int *skiplen,
int *patlen)
{
char_u *cmd;
cmdmod_T save_cmdmod = cmdmod;
@@ -230,7 +234,7 @@ do_incsearch_highlighting(int firstc, int *search_delim, incsearch_state_T *is_s
parse_command_modifiers(&ea, &dummy, TRUE);
cmdmod = save_cmdmod;
cmd = skip_range(ea.cmd, NULL);
cmd = skip_range(ea.cmd, TRUE, NULL);
if (vim_strchr((char_u *)"sgvl", *cmd) == NULL)
goto theend;
+7
View File
@@ -1934,6 +1934,13 @@ grab_file_name(long count, linenr_T *file_lnum)
if (get_visual_text(NULL, &ptr, &len) == FAIL)
return NULL;
// Only recognize ":123" here
if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1]))
{
char_u *p = ptr + len + 1;
*file_lnum = getdigits(&p);
}
return find_file_name_in_path(ptr, len, options,
count, curbuf->b_ffname);
}
+31 -32
View File
@@ -391,42 +391,41 @@ EXTERN sctx_T current_sctx INIT4(0, 0, 0, 0);
// Commonly used types.
EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, 0, NULL, NULL);
EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, 0, NULL, NULL);
EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, 0, NULL, NULL);
EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, 0, NULL, NULL);
EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, 0, NULL, NULL);
EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, 0, NULL, NULL);
EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, 0, NULL, NULL);
EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, 0, NULL, NULL);
EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, 0, NULL, NULL);
EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, 0, NULL, NULL);
EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, 0, NULL, NULL);
EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, 0, &t_unknown, NULL);
EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, 0, &t_void, NULL);
EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, 0, &t_any, NULL);
EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, 0, &t_number, NULL);
EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, 0, &t_string, NULL);
EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, 0, &t_void, NULL);
EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, 0, &t_any, NULL);
EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, 0, &t_number, NULL);
EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, 0, &t_string, NULL);
EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_unknown, NULL);
EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, NULL);
EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL);
EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_number, NULL);
EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_string, NULL);
EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_void, NULL);
EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_any, NULL);
EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_number, NULL);
EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_string, NULL);
EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, 0, &t_any, NULL);
EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, 0, &t_any, NULL);
EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, 0, &t_unknown, NULL);
EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, 0, &t_unknown, NULL);
EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_any, NULL);
EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_any, NULL);
EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_unknown, NULL);
EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_unknown, NULL);
EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, 0, &t_bool, NULL);
EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, 0, &t_number, NULL);
EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, 0, &t_string, NULL);
EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, 0, &t_dict_any, NULL);
EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, 0, &t_bool, NULL);
EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, 0, &t_number, NULL);
EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, 0, &t_string, NULL);
EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_bool, NULL);
EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_number, NULL);
EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_string, NULL);
EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_dict_any, NULL);
EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_bool, NULL);
EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_number, NULL);
EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_string, NULL);
#endif
+1 -1
View File
@@ -81,7 +81,7 @@ hash_clear(hashtab_T *ht)
vim_free(ht->ht_array);
}
#if defined(FEAT_SPELL) || defined(PROTO)
#if defined(FEAT_SPELL) || defined(FEAT_TERMINAL) || defined(PROTO)
/*
* Free the array of a hash table and all the keys it contains. The keys must
* have been allocated. "off" is the offset from the start of the allocate
+4 -2
View File
@@ -729,7 +729,8 @@ do_highlight(
if (!ends_excmd2(line, skipwhite(to_end)))
{
semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
semsg(_("E413: Too many arguments: \":highlight link %s\""),
from_start);
return;
}
@@ -1630,7 +1631,8 @@ restore_cterm_colors(void)
static int
hl_has_settings(int idx, int check_link)
{
return ( HL_TABLE()[idx].sg_term_attr != 0
return HL_TABLE()[idx].sg_cleared == 0
&& ( HL_TABLE()[idx].sg_term_attr != 0
|| HL_TABLE()[idx].sg_cterm_attr != 0
|| HL_TABLE()[idx].sg_cterm_fg != 0
|| HL_TABLE()[idx].sg_cterm_bg != 0
+9
View File
@@ -241,6 +241,9 @@ typedef int perl_key;
# else
# define Perl_sv_2pv dll_Perl_sv_2pv
# endif
# if (PERL_REVISION == 5) && (PERL_VERSION >= 32)
# define Perl_sv_2pvbyte_flags dll_Perl_sv_2pvbyte_flags
# endif
# define Perl_sv_2pvbyte dll_Perl_sv_2pvbyte
# define Perl_sv_bless dll_Perl_sv_bless
# if (PERL_REVISION == 5) && (PERL_VERSION >= 8)
@@ -397,6 +400,9 @@ static char* (*Perl_sv_2pv_nolen)(pTHX_ SV*);
static char* (*Perl_sv_2pv)(pTHX_ SV*, STRLEN*);
# endif
static char* (*Perl_sv_2pvbyte)(pTHX_ SV*, STRLEN*);
# if (PERL_REVISION == 5) && (PERL_VERSION >= 32)
static char* (*Perl_sv_2pvbyte_flags)(pTHX_ SV*, STRLEN*, I32);
# endif
static SV* (*Perl_sv_bless)(pTHX_ SV*, HV*);
# if (PERL_REVISION == 5) && (PERL_VERSION >= 8)
static void (*Perl_sv_catpvn_flags)(pTHX_ SV* , const char*, STRLEN, I32);
@@ -553,6 +559,9 @@ static struct {
{"Perl_sv_2pv", (PERL_PROC*)&Perl_sv_2pv},
# endif
{"Perl_sv_2pvbyte", (PERL_PROC*)&Perl_sv_2pvbyte},
# if (PERL_REVISION == 5) && (PERL_VERSION >= 32)
{"Perl_sv_2pvbyte_flags", (PERL_PROC*)&Perl_sv_2pvbyte_flags},
# endif
# ifdef PERL589_OR_LATER
{"Perl_sv_2iv_flags", (PERL_PROC*)&Perl_sv_2iv_flags},
{"Perl_newXS_flags", (PERL_PROC*)&Perl_newXS_flags},
+1
View File
@@ -4014,6 +4014,7 @@ ins_complete(int c, int enable_pum)
{
edit_submode_extra = (char_u *)_("The only match");
edit_submode_highl = HLF_COUNT;
compl_curr_match->cp_number = 0;
}
else
{
+30 -23
View File
@@ -817,7 +817,7 @@ f_flatten(typval_T *argvars, typval_T *rettv)
}
l = argvars[0].vval.v_list;
if (l != NULL && !var_check_lock(l->lv_lock,
if (l != NULL && !value_check_lock(l->lv_lock,
(char_u *)N_("flatten() argument"), TRUE)
&& list_flatten(l, maxdepth) == OK)
copy_tv(&argvars[0], rettv);
@@ -1439,7 +1439,7 @@ list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
int idx;
if ((l = argvars[0].vval.v_list) == NULL
|| var_check_lock(l->lv_lock, arg_errmsg, TRUE))
|| value_check_lock(l->lv_lock, arg_errmsg, TRUE))
return;
idx = (long)tv_get_number_chk(&argvars[1], &error);
@@ -1687,7 +1687,7 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
else
{
l = argvars[0].vval.v_list;
if (l == NULL || var_check_lock(l->lv_lock,
if (l == NULL || value_check_lock(l->lv_lock,
(char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
TRUE))
goto theend;
@@ -1717,18 +1717,25 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
else
{
int error = FALSE;
int nr = 0;
i = (long)tv_get_number_chk(&argvars[1], &error);
if (error)
goto theend; // type error; errmsg already given
if (i == 1)
info.item_compare_ic = TRUE;
else if (argvars[1].v_type != VAR_NUMBER)
info.item_compare_func = tv_get_string(&argvars[1]);
else if (i != 0)
if (argvars[1].v_type == VAR_NUMBER)
{
emsg(_(e_invarg));
goto theend;
nr = tv_get_number_chk(&argvars[1], &error);
if (error)
goto theend; // type error; errmsg already given
if (nr == 1)
info.item_compare_ic = TRUE;
}
if (nr != 1)
{
if (argvars[1].v_type != VAR_NUMBER)
info.item_compare_func = tv_get_string(&argvars[1]);
else if (nr != 0)
{
emsg(_(e_invarg));
goto theend;
}
}
if (info.item_compare_func != NULL)
{
@@ -1955,13 +1962,13 @@ filter_map(typval_T *argvars, typval_T *rettv, int map)
else if (argvars[0].v_type == VAR_LIST)
{
if ((l = argvars[0].vval.v_list) == NULL
|| (!map && var_check_lock(l->lv_lock, arg_errmsg, TRUE)))
|| (!map && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
return;
}
else if (argvars[0].v_type == VAR_DICT)
{
if ((d = argvars[0].vval.v_dict) == NULL
|| (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE)))
|| (!map && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
return;
}
else
@@ -2004,7 +2011,7 @@ filter_map(typval_T *argvars, typval_T *rettv, int map)
--todo;
di = HI2DI(hi);
if (map && (var_check_lock(di->di_tv.v_lock,
if (map && (value_check_lock(di->di_tv.v_lock,
arg_errmsg, TRUE)
|| var_check_ro(di->di_flags,
arg_errmsg, TRUE)))
@@ -2077,7 +2084,7 @@ filter_map(typval_T *argvars, typval_T *rettv, int map)
l->lv_lock = VAR_LOCKED;
for (li = l->lv_first; li != NULL; li = nli)
{
if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
if (map && value_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
break;
nli = li->li_next;
set_vim_var_nr(VV_KEY, idx);
@@ -2131,7 +2138,7 @@ f_add(typval_T *argvars, typval_T *rettv)
if (argvars[0].v_type == VAR_LIST)
{
if ((l = argvars[0].vval.v_list) != NULL
&& !var_check_lock(l->lv_lock,
&& !value_check_lock(l->lv_lock,
(char_u *)N_("add() argument"), TRUE)
&& list_append_tv(l, &argvars[1]) == OK)
copy_tv(&argvars[0], rettv);
@@ -2139,7 +2146,7 @@ f_add(typval_T *argvars, typval_T *rettv)
else if (argvars[0].v_type == VAR_BLOB)
{
if ((b = argvars[0].vval.v_blob) != NULL
&& !var_check_lock(b->bv_lock,
&& !value_check_lock(b->bv_lock,
(char_u *)N_("add() argument"), TRUE))
{
int error = FALSE;
@@ -2282,7 +2289,7 @@ f_extend(typval_T *argvars, typval_T *rettv)
l1 = argvars[0].vval.v_list;
l2 = argvars[1].vval.v_list;
if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE)
if (l1 != NULL && !value_check_lock(l1->lv_lock, arg_errmsg, TRUE)
&& l2 != NULL)
{
if (argvars[2].v_type != VAR_UNKNOWN)
@@ -2318,7 +2325,7 @@ f_extend(typval_T *argvars, typval_T *rettv)
d1 = argvars[0].vval.v_dict;
d2 = argvars[1].vval.v_dict;
if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE)
if (d1 != NULL && !value_check_lock(d1->dv_lock, arg_errmsg, TRUE)
&& d2 != NULL)
{
// Check the third argument.
@@ -2402,7 +2409,7 @@ f_insert(typval_T *argvars, typval_T *rettv)
else if (argvars[0].v_type != VAR_LIST)
semsg(_(e_listblobarg), "insert()");
else if ((l = argvars[0].vval.v_list) != NULL
&& !var_check_lock(l->lv_lock,
&& !value_check_lock(l->lv_lock,
(char_u *)N_("insert() argument"), TRUE))
{
if (argvars[2].v_type != VAR_UNKNOWN)
@@ -2475,7 +2482,7 @@ f_reverse(typval_T *argvars, typval_T *rettv)
if (argvars[0].v_type != VAR_LIST)
semsg(_(e_listblobarg), "reverse()");
else if ((l = argvars[0].vval.v_list) != NULL
&& !var_check_lock(l->lv_lock,
&& !value_check_lock(l->lv_lock,
(char_u *)N_("reverse() argument"), TRUE))
{
if (l->lv_first == &range_list_item)
+1 -1
View File
@@ -2634,7 +2634,7 @@ add_text_props_for_append(
int n;
char_u *props;
int new_len = 0; // init for gcc
char_u *new_line;
char_u *new_line = NULL;
textprop_T prop;
// Make two rounds:
+1 -1
View File
@@ -461,7 +461,7 @@ get_emsg_source(void)
if (SOURCING_NAME != NULL && other_sourcing_name())
{
char_u *sname = estack_sfile(FALSE);
char_u *sname = estack_sfile(ESTACK_NONE);
char_u *tofree = sname;
if (sname == NULL)
+40 -5
View File
@@ -481,6 +481,7 @@ block_insert(
int count = 0; // extra spaces to replace a cut TAB
int spaces = 0; // non-zero if cutting a TAB
colnr_T offset; // pointer along new line
colnr_T startcol; // column where insert starts
unsigned s_len; // STRLEN(s)
char_u *newp, *oldp; // new, old lines
linenr_T lnum; // loop var
@@ -553,9 +554,10 @@ block_insert(
// insert pre-padding
vim_memset(newp + offset, ' ', (size_t)spaces);
startcol = offset + spaces;
// copy the new text
mch_memmove(newp + offset + spaces, s, (size_t)s_len);
mch_memmove(newp + startcol, s, (size_t)s_len);
offset += s_len;
if (spaces && !bdp->is_short)
@@ -574,6 +576,10 @@ block_insert(
ml_replace(lnum, newp, FALSE);
if (b_insert)
// correct any text properties
inserted_bytes(lnum, startcol, s_len);
if (lnum == oap->end.lnum)
{
// Set "']" mark to the end of the block instead of the end of
@@ -2636,6 +2642,9 @@ do_addsub(
}
else
{
pos_T save_pos;
int i;
if (col > 0 && ptr[col - 1] == '-'
&& (!has_mbyte ||
!(*mb_head_off)(ptr, ptr + col - 1))
@@ -2734,7 +2743,9 @@ do_addsub(
*/
if (c == '-')
--length;
while (todel-- > 0)
save_pos = curwin->w_cursor;
for (i = 0; i < todel; ++i)
{
if (c < 0x100 && isalpha(c))
{
@@ -2743,10 +2754,10 @@ do_addsub(
else
hexupper = FALSE;
}
// del_char() will mark line needing displaying
(void)del_char(FALSE);
inc_cursor();
c = gchar_cursor();
}
curwin->w_cursor = save_pos;
/*
* Prepare the leading characters in buf1[].
@@ -2776,7 +2787,6 @@ do_addsub(
*/
if (pre == 'b' || pre == 'B')
{
int i;
int bit = 0;
int bits = sizeof(uvarnumber_T) * 8;
@@ -2809,9 +2819,34 @@ do_addsub(
while (length-- > 0)
*ptr++ = '0';
*ptr = NUL;
STRCAT(buf1, buf2);
// Insert just after the first character to be removed, so that any
// text properties will be adjusted. Then delete the old number
// afterwards.
save_pos = curwin->w_cursor;
if (todel > 0)
inc_cursor();
ins_str(buf1); // insert the new number
vim_free(buf1);
// del_char() will also mark line needing displaying
if (todel > 0)
{
int bytes_after = (int)STRLEN(ml_get_curline())
- curwin->w_cursor.col;
// Delete the one character before the insert.
curwin->w_cursor = save_pos;
(void)del_char(FALSE);
curwin->w_cursor.col = (colnr_T)(STRLEN(ml_get_curline())
- bytes_after);
--todel;
}
while (todel-- > 0)
(void)del_char(FALSE);
endpos = curwin->w_cursor;
if (did_change && curwin->w_cursor.col)
--curwin->w_cursor.col;
+60 -31
View File
@@ -37,6 +37,7 @@
static void set_options_default(int opt_flags);
static void set_string_default_esc(char *name, char_u *val, int escape);
static char_u *find_dup_item(char_u *origval, char_u *newval, long_u flags);
static char_u *option_expand(int opt_idx, char_u *val);
static void didset_options(void);
static void didset_options2(void);
@@ -142,6 +143,9 @@ set_init_1(int clean_arg)
int len;
garray_T ga;
int mustfree;
char_u *item;
opt_idx = findoption((char_u *)"backupskip");
ga_init2(&ga, 1, 100);
for (n = 0; n < (long)(sizeof(names) / sizeof(char *)); ++n)
@@ -161,15 +165,20 @@ set_init_1(int clean_arg)
{
// First time count the NUL, otherwise count the ','.
len = (int)STRLEN(p) + 3;
if (ga_grow(&ga, len) == OK)
item = alloc(len);
STRCPY(item, p);
add_pathsep(item);
STRCAT(item, "*");
if (find_dup_item(ga.ga_data, item, options[opt_idx].flags)
== NULL
&& ga_grow(&ga, len) == OK)
{
if (ga.ga_len > 0)
STRCAT(ga.ga_data, ",");
STRCAT(ga.ga_data, p);
add_pathsep(ga.ga_data);
STRCAT(ga.ga_data, "*");
STRCAT(ga.ga_data, item);
ga.ga_len += len;
}
vim_free(item);
}
if (mustfree)
vim_free(p);
@@ -685,6 +694,46 @@ set_string_default(char *name, char_u *val)
set_string_default_esc(name, val, FALSE);
}
/*
* For an option value that contains comma separated items, find "newval" in
* "origval". Return NULL if not found.
*/
static char_u *
find_dup_item(char_u *origval, char_u *newval, long_u flags)
{
int bs = 0;
size_t newlen;
char_u *s;
if (origval == NULL)
return NULL;
newlen = STRLEN(newval);
for (s = origval; *s != NUL; ++s)
{
if ((!(flags & P_COMMA)
|| s == origval
|| (s[-1] == ',' && !(bs & 1)))
&& STRNCMP(s, newval, newlen) == 0
&& (!(flags & P_COMMA)
|| s[newlen] == ','
|| s[newlen] == NUL))
return s;
// Count backslashes. Only a comma with an even number of backslashes
// or a single backslash preceded by a comma before it is recognized as
// a separator.
if ((s > origval + 1
&& s[-1] == '\\'
&& s[-2] != ',')
|| (s == origval + 1
&& s[-1] == '\\'))
++bs;
else
bs = 0;
}
return NULL;
}
/*
* Set the Vi-default value of a number option.
* Used for 'lines' and 'columns'.
@@ -1590,7 +1639,6 @@ do_set(
#endif
unsigned newlen;
int comma;
int bs;
int new_value_alloced; // new string option
// was allocated
@@ -1829,39 +1877,20 @@ do_set(
if (removing || (flags & P_NODUP))
{
i = (int)STRLEN(newval);
bs = 0;
for (s = origval; *s; ++s)
{
if ((!(flags & P_COMMA)
|| s == origval
|| (s[-1] == ',' && !(bs & 1)))
&& STRNCMP(s, newval, i) == 0
&& (!(flags & P_COMMA)
|| s[i] == ','
|| s[i] == NUL))
break;
// Count backslashes. Only a comma with an
// even number of backslashes or a single
// backslash preceded by a comma before it
// is recognized as a separator
if ((s > origval + 1
&& s[-1] == '\\'
&& s[-2] != ',')
|| (s == origval + 1
&& s[-1] == '\\'))
++bs;
else
bs = 0;
}
s = find_dup_item(origval, newval, flags);
// do not add if already there
if ((adding || prepending) && *s)
if ((adding || prepending) && s != NULL)
{
prepending = FALSE;
adding = FALSE;
STRCPY(newval, origval);
}
// if no duplicate, move pointer to end of
// original value
if (s == NULL)
s = origval + (int)STRLEN(origval);
}
// concatenate the two strings; add a ',' if
+2 -2
View File
@@ -7456,7 +7456,7 @@ mch_libcall(
)))
{
if (string_result == NULL)
retval_int = ((STRPROCINT)ProcAdd)(argstring);
retval_int = ((STRPROCINT)(void *)ProcAdd)(argstring);
else
retval_str = (ProcAdd)(argstring);
}
@@ -7478,7 +7478,7 @@ mch_libcall(
)))
{
if (string_result == NULL)
retval_int = ((INTPROCINT)ProcAddI)(argint);
retval_int = ((INTPROCINT)(void *)ProcAddI)(argint);
else
retval_str = (ProcAddI)(argint);
}
+1
View File
@@ -9,6 +9,7 @@ void may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added);
void invoke_listeners(buf_T *buf);
void remove_listeners(buf_T *buf);
void changed_bytes(linenr_T lnum, colnr_T col);
void inserted_bytes(linenr_T lnum, colnr_T col, int added);
void appended_lines(linenr_T lnum, long count);
void appended_lines_mark(linenr_T lnum, long count);
void deleted_lines(linenr_T lnum, long count);
+4 -2
View File
@@ -23,6 +23,7 @@ void ex_unlet(exarg_T *eap);
void ex_lockvar(exarg_T *eap);
void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie);
int do_unlet(char_u *name, int forceit);
void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
void del_menutrans_vars(void);
char_u *get_user_var_name(expand_T *xp, int idx);
char *get_var_special_name(int nr);
@@ -65,11 +66,12 @@ void unref_var_dict(dict_T *dict);
void vars_clear(hashtab_T *ht);
void vars_clear_ext(hashtab_T *ht, int free_val);
void set_var(char_u *name, typval_T *tv, int copy);
void set_var_const(char_u *name, type_T *type, typval_T *tv, int copy, int flags);
void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags);
int var_check_ro(int flags, char_u *name, int use_gettext);
int var_check_lock(int flags, char_u *name, int use_gettext);
int var_check_fixed(int flags, char_u *name, int use_gettext);
int var_wrong_func_name(char_u *name, int new_var);
int var_check_lock(int lock, char_u *name, int use_gettext);
int value_check_lock(int lock, char_u *name, int use_gettext);
int valid_varname(char_u *varname);
void reset_v_option_vars(void);
void assert_error(garray_T *gap);
+1 -1
View File
@@ -16,7 +16,7 @@ int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
long excmd_get_argt(cmdidx_T idx);
char_u *skip_range(char_u *cmd, int *ctx);
char_u *skip_range(char_u *cmd, int skip_star, int *ctx);
void ex_ni(exarg_T *eap);
int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp);
void separate_nextcmd(exarg_T *eap);
+1 -1
View File
@@ -4,7 +4,7 @@ estack_T *estack_push(etype_T type, char_u *name, long lnum);
estack_T *estack_push_ufunc(ufunc_T *ufunc, long lnum);
int estack_top_is_ufunc(ufunc_T *ufunc, long lnum);
estack_T *estack_pop(void);
char_u *estack_sfile(int is_sfile);
char_u *estack_sfile(estack_arg_T which);
void ex_runtime(exarg_T *eap);
int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
+1
View File
@@ -36,4 +36,5 @@ void find_pattern_in_path(char_u *ptr, int dir, int len, int whole, int skip_com
spat_T *get_spat(int idx);
int get_spat_last_idx(void);
void f_searchcount(typval_T *argvars, typval_T *rettv);
void f_matchfuzzy(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */
+1 -1
View File
@@ -2,7 +2,7 @@
void init_job_options(jobopt_T *opt);
buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags);
void ex_terminal(exarg_T *eap);
int term_write_session(FILE *fd, win_T *wp);
int term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs);
int term_should_restore(buf_T *buf);
void free_terminal(buf_T *buf);
void free_unused_terminals(void);
+1
View File
@@ -11,6 +11,7 @@ int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T
char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx);
ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
int func_is_global(ufunc_T *ufunc);
void copy_func(char_u *lambda, char_u *global);
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
void save_funccal(funccal_entry_T *entry);
+3 -1
View File
@@ -1,6 +1,8 @@
/* vim9type.c */
type_T *alloc_type(garray_T *type_gap);
type_T *get_type_ptr(garray_T *type_gap);
void clear_type_list(garray_T *gap);
type_T *alloc_type(type_T *type);
void free_type(type_T *type);
type_T *get_list_type(type_T *member_type, garray_T *type_gap);
type_T *get_dict_type(type_T *member_type, garray_T *type_gap);
type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap);
+36 -5
View File
@@ -216,7 +216,9 @@ static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
static char_u *qf_last_bufname = NULL;
static bufref_T qf_last_bufref = {NULL, 0, 0};
static char *e_loc_list_changed =
static char *e_current_quickfix_list_was_changed =
N_("E925: Current quickfix list was changed");
static char *e_current_location_list_was_changed =
N_("E926: Current location list was changed");
/*
@@ -3108,6 +3110,7 @@ qf_jump_edit_buffer(
int *opened_window)
{
qf_list_T *qfl = qf_get_curlist(qi);
int old_changedtick = qfl->qf_changedtick;
qfltype_T qfl_type = qfl->qfl_type;
int retval = OK;
int old_qf_curlist = qi->qf_curlist;
@@ -3146,17 +3149,20 @@ qf_jump_edit_buffer(
if (qfl_type == QFLT_QUICKFIX && !qflist_valid(NULL, save_qfid))
{
emsg(_("E925: Current quickfix was changed"));
emsg(_(e_current_quickfix_list_was_changed));
return NOTDONE;
}
// Check if the list was changed. The pointers may happen to be identical,
// thus also check qf_changedtick.
if (old_qf_curlist != qi->qf_curlist
|| old_changedtick != qfl->qf_changedtick
|| !is_qf_entry_present(qfl, qf_ptr))
{
if (qfl_type == QFLT_QUICKFIX)
emsg(_("E925: Current quickfix was changed"));
emsg(_(e_current_quickfix_list_was_changed));
else
emsg(_(e_loc_list_changed));
emsg(_(e_current_location_list_was_changed));
return NOTDONE;
}
@@ -3264,10 +3270,25 @@ qf_jump_open_window(
int newwin,
int *opened_window)
{
qf_list_T *qfl = qf_get_curlist(qi);
int old_changedtick = qfl->qf_changedtick;
int old_qf_curlist = qi->qf_curlist;
qfltype_T qfl_type = qfl->qfl_type;
// For ":helpgrep" find a help window or open one.
if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0))
if (jump_to_help_window(qi, newwin, opened_window) == FAIL)
return FAIL;
if (old_qf_curlist != qi->qf_curlist
|| old_changedtick != qfl->qf_changedtick
|| !is_qf_entry_present(qfl, qf_ptr))
{
if (qfl_type == QFLT_QUICKFIX)
emsg(_(e_current_quickfix_list_was_changed));
else
emsg(_(e_current_location_list_was_changed));
return FAIL;
}
// If currently in the quickfix window, find another window to show the
// file in.
@@ -3282,6 +3303,16 @@ qf_jump_open_window(
opened_window) == FAIL)
return FAIL;
}
if (old_qf_curlist != qi->qf_curlist
|| old_changedtick != qfl->qf_changedtick
|| !is_qf_entry_present(qfl, qf_ptr))
{
if (qfl_type == QFLT_QUICKFIX)
emsg(_(e_current_quickfix_list_was_changed));
else
emsg(_(e_current_location_list_was_changed));
return FAIL;
}
return OK;
}
@@ -5834,7 +5865,7 @@ vgr_qflist_valid(
if (wp != NULL)
{
// An autocmd has freed the location list.
emsg(_(e_loc_list_changed));
emsg(_(e_current_location_list_was_changed));
return FALSE;
}
else
+19 -11
View File
@@ -111,10 +111,10 @@ estack_pop(void)
/*
* Get the current value for <sfile> in allocated memory.
* "is_sfile" is TRUE for <sfile> itself.
* "which" is ESTACK_SFILE for <sfile> and ESTACK_STACK for <stack>.
*/
char_u *
estack_sfile(int is_sfile UNUSED)
estack_sfile(estack_arg_T which UNUSED)
{
estack_T *entry;
#ifdef FEAT_EVAL
@@ -127,7 +127,7 @@ estack_sfile(int is_sfile UNUSED)
entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
#ifdef FEAT_EVAL
if (is_sfile && entry->es_type != ETYPE_UFUNC)
if (which == ESTACK_SFILE && entry->es_type != ETYPE_UFUNC)
#endif
{
if (entry->es_name == NULL)
@@ -144,6 +144,9 @@ estack_sfile(int is_sfile UNUSED)
entry = ((estack_T *)exestack.ga_data) + idx;
if (entry->es_name != NULL)
{
long lnum = 0;
char *dots;
len = STRLEN(entry->es_name) + 15;
type_name = "";
if (entry->es_type != last_type)
@@ -159,15 +162,20 @@ estack_sfile(int is_sfile UNUSED)
len += STRLEN(type_name);
if (ga_grow(&ga, (int)len) == FAIL)
break;
if (idx == exestack.ga_len - 1 || entry->es_lnum == 0)
// For the bottom entry: do not add the line number, it is used
// in <slnum>. Also leave it out when the number is not set.
vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s%s",
type_name, entry->es_name,
idx == exestack.ga_len - 1 ? "" : "..");
if (idx == exestack.ga_len - 1)
lnum = which == ESTACK_STACK ? SOURCING_LNUM : 0;
else
vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s[%ld]..",
type_name, entry->es_name, entry->es_lnum);
lnum = entry->es_lnum;
dots = idx == exestack.ga_len - 1 ? "" : "..";
if (lnum == 0)
// For the bottom entry of <sfile>: do not add the line number,
// it is used in <slnum>. Also leave it out when the number is
// not set.
vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s%s",
type_name, entry->es_name, dots);
else
vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s[%ld]%s",
type_name, entry->es_name, lnum, dots);
ga.ga_len += (int)STRLEN((char *)ga.ga_data + ga.ga_len);
}
}
+340
View File
@@ -4180,4 +4180,344 @@ f_searchcount(typval_T *argvars, typval_T *rettv)
the_end:
restore_last_search_pattern();
}
/*
* Fuzzy string matching
*
* Ported from the lib_fts library authored by Forrest Smith.
* https://github.com/forrestthewoods/lib_fts/tree/master/code
*
* Blog describing the algorithm:
* https://www.forrestthewoods.com/blog/reverse_engineering_sublime_texts_fuzzy_match/
*
* Each matching string is assigned a score. The following factors are checked:
* Matched letter
* Unmatched letter
* Consecutively matched letters
* Proximity to start
* Letter following a separator (space, underscore)
* Uppercase letter following lowercase (aka CamelCase)
*
* Matched letters are good. Unmatched letters are bad. Matching near the start
* is good. Matching the first letter in the middle of a phrase is good.
* Matching the uppercase letters in camel case entries is good.
*
* The score assigned for each factor is explained below.
* File paths are different from file names. File extensions may be ignorable.
* Single words care about consecutive matches but not separators or camel
* case.
* Score starts at 0
* Matched letter: +0 points
* Unmatched letter: -1 point
* Consecutive match bonus: +5 points
* Separator bonus: +10 points
* Camel case bonus: +10 points
* Unmatched leading letter: -3 points (max: -9)
*
* There is some nuance to this. Scores dont have an intrinsic meaning. The
* score range isnt 0 to 100. Its roughly [-50, 50]. Longer words have a
* lower minimum score due to unmatched letter penalty. Longer search patterns
* have a higher maximum score due to match bonuses.
*
* Separator and camel case bonus is worth a LOT. Consecutive matches are worth
* quite a bit.
*
* There is a penalty if you DONT match the first three letters. Which
* effectively rewards matching near the start. However theres no difference
* in matching between the middle and end.
*
* There is not an explicit bonus for an exact match. Unmatched letters receive
* a penalty. So shorter strings and closer matches are worth more.
*/
typedef struct
{
listitem_T *item;
int score;
} fuzzyItem_T;
static int
fuzzy_match_recursive(
char_u *fuzpat,
char_u *str,
int *outScore,
char_u *strBegin,
char_u *srcMatches,
char_u *matches,
int maxMatches,
int nextMatch,
int *recursionCount,
int recursionLimit)
{
// Recursion params
int recursiveMatch = FALSE;
char_u bestRecursiveMatches[256];
int bestRecursiveScore = 0;
int first_match;
int matched;
// Count recursions
++*recursionCount;
if (*recursionCount >= recursionLimit)
return FALSE;
// Detect end of strings
if (*fuzpat == '\0' || *str == '\0')
return FALSE;
// Loop through fuzpat and str looking for a match
first_match = TRUE;
while (*fuzpat != '\0' && *str != '\0')
{
// Found match
if (vim_tolower(*fuzpat) == vim_tolower(*str))
{
char_u recursiveMatches[256];
int recursiveScore = 0;
// Supplied matches buffer was too short
if (nextMatch >= maxMatches)
return FALSE;
// "Copy-on-Write" srcMatches into matches
if (first_match && srcMatches)
{
memcpy(matches, srcMatches, nextMatch);
first_match = FALSE;
}
// Recursive call that "skips" this match
if (fuzzy_match_recursive(fuzpat, str + 1, &recursiveScore,
strBegin, matches, recursiveMatches,
sizeof(recursiveMatches), nextMatch, recursionCount,
recursionLimit))
{
// Pick best recursive score
if (!recursiveMatch || recursiveScore > bestRecursiveScore)
{
memcpy(bestRecursiveMatches, recursiveMatches, 256);
bestRecursiveScore = recursiveScore;
}
recursiveMatch = TRUE;
}
// Advance
matches[nextMatch++] = (char_u)(str - strBegin);
++fuzpat;
}
++str;
}
// Determine if full fuzpat was matched
matched = *fuzpat == '\0' ? TRUE : FALSE;
// Calculate score
if (matched)
{
// bonus for adjacent matches
int sequential_bonus = 15;
// bonus if match occurs after a separator
int separator_bonus = 30;
// bonus if match is uppercase and prev is lower
int camel_bonus = 30;
// bonus if the first letter is matched
int first_letter_bonus = 15;
// penalty applied for every letter in str before the first match
int leading_letter_penalty = -5;
// maximum penalty for leading letters
int max_leading_letter_penalty = -15;
// penalty for every letter that doesn't matter
int unmatched_letter_penalty = -1;
int penalty;
int unmatched;
int i;
// Iterate str to end
while (*str != '\0')
++str;
// Initialize score
*outScore = 100;
// Apply leading letter penalty
penalty = leading_letter_penalty * matches[0];
if (penalty < max_leading_letter_penalty)
penalty = max_leading_letter_penalty;
*outScore += penalty;
// Apply unmatched penalty
unmatched = (int)(str - strBegin) - nextMatch;
*outScore += unmatched_letter_penalty * unmatched;
// Apply ordering bonuses
for (i = 0; i < nextMatch; ++i)
{
char_u currIdx = matches[i];
if (i > 0)
{
char_u prevIdx = matches[i - 1];
// Sequential
if (currIdx == (prevIdx + 1))
*outScore += sequential_bonus;
}
// Check for bonuses based on neighbor character value
if (currIdx > 0)
{
// Camel case
char_u neighbor = strBegin[currIdx - 1];
char_u curr = strBegin[currIdx];
int neighborSeparator;
if (islower(neighbor) && isupper(curr))
*outScore += camel_bonus;
// Separator
neighborSeparator = neighbor == '_' || neighbor == ' ';
if (neighborSeparator)
*outScore += separator_bonus;
}
else
{
// First letter
*outScore += first_letter_bonus;
}
}
}
// Return best result
if (recursiveMatch && (!matched || bestRecursiveScore > *outScore))
{
// Recursive score is better than "this"
memcpy(matches, bestRecursiveMatches, maxMatches);
*outScore = bestRecursiveScore;
return TRUE;
}
else if (matched)
return TRUE; // "this" score is better than recursive
return FALSE; // no match
}
/*
* fuzzy_match()
*
* Performs exhaustive search via recursion to find all possible matches and
* match with highest score.
* Scores values have no intrinsic meaning. Possible score range is not
* normalized and varies with pattern.
* Recursion is limited internally (default=10) to prevent degenerate cases
* (fuzpat="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").
* Uses char_u for match indices. Therefore patterns are limited to 256
* characters.
*
* Returns TRUE if fuzpat is found AND calculates a score.
*/
static int
fuzzy_match(char_u *str, char_u *fuzpat, int *outScore)
{
char_u matches[256];
int recursionCount = 0;
int recursionLimit = 10;
*outScore = 0;
return fuzzy_match_recursive(fuzpat, str, outScore, str, NULL, matches,
sizeof(matches), 0, &recursionCount, recursionLimit);
}
/*
* Sort the fuzzy matches in the descending order of the match score.
*/
static int
fuzzy_item_compare(const void *s1, const void *s2)
{
int v1 = ((fuzzyItem_T *)s1)->score;
int v2 = ((fuzzyItem_T *)s2)->score;
return v1 == v2 ? 0 : v1 > v2 ? -1 : 1;
}
/*
* Fuzzy search the string 'str' in 'strlist' and return the matching strings
* in 'fmatchlist'.
*/
static void
match_fuzzy(list_T *strlist, char_u *str, list_T *fmatchlist)
{
long len;
fuzzyItem_T *ptrs;
listitem_T *li;
long i = 0;
int found_match = FALSE;
len = list_len(strlist);
if (len == 0)
return;
ptrs = ALLOC_MULT(fuzzyItem_T, len);
if (ptrs == NULL)
return;
// For all the string items in strlist, get the fuzzy matching score
FOR_ALL_LIST_ITEMS(strlist, li)
{
int score;
ptrs[i].item = li;
ptrs[i].score = -9999;
// ignore non-string items in the list
if (li->li_tv.v_type == VAR_STRING && li->li_tv.vval.v_string != NULL)
if (fuzzy_match(li->li_tv.vval.v_string, str, &score))
{
ptrs[i].score = score;
found_match = TRUE;
}
++i;
}
if (found_match)
{
// Sort the list by the descending order of the match score
qsort((void *)ptrs, (size_t)len, sizeof(fuzzyItem_T),
fuzzy_item_compare);
// Copy the matching strings with 'score != -9999' to the return list
for (i = 0; i < len; i++)
{
if (ptrs[i].score == -9999)
break;
list_append_string(fmatchlist, ptrs[i].item->li_tv.vval.v_string,
-1);
}
}
vim_free(ptrs);
}
/*
* "matchfuzzy()" function
*/
void
f_matchfuzzy(typval_T *argvars, typval_T *rettv)
{
if (argvars[0].v_type != VAR_LIST)
{
emsg(_(e_listreq));
return;
}
if (argvars[0].vval.v_list == NULL)
return;
if (argvars[1].v_type != VAR_STRING
|| argvars[1].vval.v_string == NULL)
{
semsg(_(e_invarg2), tv_get_string(&argvars[1]));
return;
}
if (rettv_list_alloc(rettv) == OK)
match_fuzzy(argvars[0].vval.v_list, tv_get_string(&argvars[1]),
rettv->vval.v_list);
}
#endif
+70 -52
View File
@@ -303,10 +303,12 @@ put_view_curpos(FILE *fd, win_T *wp, char *spaces)
put_view(
FILE *fd,
win_T *wp,
int add_edit, // add ":edit" command to view
unsigned *flagp, // vop_flags or ssop_flags
int current_arg_idx) // current argument index of the window, use
// -1 if unknown
int add_edit, // add ":edit" command to view
unsigned *flagp, // vop_flags or ssop_flags
int current_arg_idx, // current argument index of the window,
// use -1 if unknown
hashtab_T *terminal_bufs UNUSED) // already encountered terminal buffers,
// can be NULL
{
win_T *save_curwin;
int f;
@@ -349,7 +351,7 @@ put_view(
# ifdef FEAT_TERMINAL
if (bt_terminal(wp->w_buffer))
{
if (term_write_session(fd, wp) == FAIL)
if (term_write_session(fd, wp, terminal_bufs) == FAIL)
return FAIL;
}
else
@@ -588,6 +590,12 @@ makeopens(
frame_T *tab_topframe;
int cur_arg_idx = 0;
int next_arg_idx = 0;
int ret = FAIL;
#ifdef FEAT_TERMINAL
hashtab_T terminal_bufs;
hash_init(&terminal_bufs);
#endif
if (ssop_flags & SSOP_BUFFERS)
only_save_windows = FALSE; // Save ALL buffers
@@ -596,25 +604,25 @@ makeopens(
// sessionable variables.
#ifdef FEAT_EVAL
if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL)
return FAIL;
goto fail;
if (ssop_flags & SSOP_GLOBALS)
if (store_session_globals(fd) == FAIL)
return FAIL;
goto fail;
#endif
// Close all windows and tabs but one.
if (put_line(fd, "silent only") == FAIL)
return FAIL;
goto fail;
if ((ssop_flags & SSOP_TABPAGES)
&& put_line(fd, "silent tabonly") == FAIL)
return FAIL;
goto fail;
// Now a :cd command to the session directory or the current directory
if (ssop_flags & SSOP_SESDIR)
{
if (put_line(fd, "exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')")
== FAIL)
return FAIL;
goto fail;
}
else if (ssop_flags & SSOP_CURDIR)
{
@@ -625,7 +633,7 @@ makeopens(
|| put_eol(fd) == FAIL)
{
vim_free(sname);
return FAIL;
goto fail;
}
vim_free(sname);
}
@@ -633,33 +641,33 @@ makeopens(
// If there is an empty, unnamed buffer we will wipe it out later.
// Remember the buffer number.
if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") == FAIL)
return FAIL;
goto fail;
if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL)
return FAIL;
goto fail;
if (put_line(fd, "endif") == FAIL)
return FAIL;
goto fail;
// Now save the current files, current buffer first.
if (put_line(fd, "set shortmess=aoO") == FAIL)
return FAIL;
goto fail;
// the global argument list
if (ses_arglist(fd, "argglobal", &global_alist.al_ga,
!(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL)
return FAIL;
goto fail;
if (ssop_flags & SSOP_RESIZE)
{
// Note: after the restore we still check it worked!
if (fprintf(fd, "set lines=%ld columns=%ld" , Rows, Columns) < 0
|| put_eol(fd) == FAIL)
return FAIL;
goto fail;
#ifdef FEAT_FULLSCREEN
// fullscreen needs to be set after lines and columns
if (p_fullscreen)
{
if (fprintf(fd, "set fullscreen") < 0 || put_eol(fd) == FAIL)
return FAIL;
goto fail;
}
#endif
}
@@ -673,7 +681,7 @@ makeopens(
{
// Note: after the restore we still check it worked!
if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL)
return FAIL;
goto fail;
}
}
#endif
@@ -685,7 +693,7 @@ makeopens(
if (p_stal == 1 && first_tabpage->tp_next != NULL)
{
if (put_line(fd, "set stal=2") == FAIL)
return FAIL;
goto fail;
restore_stal = TRUE;
}
@@ -703,9 +711,9 @@ makeopens(
// later local options won't be copied to the new tabs.
FOR_ALL_TABPAGES(tp)
if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL)
return FAIL;
goto fail;
if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL)
return FAIL;
goto fail;
}
for (tabnr = 1; ; ++tabnr)
{
@@ -747,13 +755,13 @@ makeopens(
)
{
if (need_tabnext && put_line(fd, "tabnext") == FAIL)
return FAIL;
goto fail;
need_tabnext = FALSE;
if (fputs("edit ", fd) < 0
|| ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE)
== FAIL)
return FAIL;
goto fail;
if (!wp->w_arg_idx_invalid)
edited_win = wp;
break;
@@ -762,17 +770,17 @@ makeopens(
// If no file got edited create an empty tab page.
if (need_tabnext && put_line(fd, "tabnext") == FAIL)
return FAIL;
goto fail;
// Save current window layout.
if (put_line(fd, "set splitbelow splitright") == FAIL)
return FAIL;
goto fail;
if (ses_win_rec(fd, tab_topframe) == FAIL)
return FAIL;
goto fail;
if (!p_sb && put_line(fd, "set nosplitbelow") == FAIL)
return FAIL;
goto fail;
if (!p_spr && put_line(fd, "set nosplitright") == FAIL)
return FAIL;
goto fail;
// Check if window sizes can be restored (no windows omitted).
// Remember the window number of the current window after restoring.
@@ -789,7 +797,7 @@ makeopens(
// Go to the first window.
if (put_line(fd, "wincmd t") == FAIL)
return FAIL;
goto fail;
// If more than one window, see if sizes can be restored.
// First set 'winheight' and 'winwidth' to 1 to avoid the windows being
@@ -802,9 +810,9 @@ makeopens(
|| put_line(fd, "set winheight=1") == FAIL
|| put_line(fd, "set winminwidth=0") == FAIL
|| put_line(fd, "set winwidth=1") == FAIL)
return FAIL;
goto fail;
if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
return FAIL;
goto fail;
// Restore the tab-local working directory if specified
// Do this before the windows, so that the window-local directory can
@@ -814,7 +822,7 @@ makeopens(
if (fputs("tcd ", fd) < 0
|| ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
|| put_eol(fd) == FAIL)
return FAIL;
goto fail;
did_lcd = TRUE;
}
@@ -823,11 +831,16 @@ makeopens(
{
if (!ses_do_win(wp))
continue;
if (put_view(fd, wp, wp != edited_win, &ssop_flags,
cur_arg_idx) == FAIL)
return FAIL;
if (put_view(fd, wp, wp != edited_win, &ssop_flags, cur_arg_idx,
#ifdef FEAT_TERMINAL
&terminal_bufs
#else
NULL
#endif
) == FAIL)
goto fail;
if (nr > 1 && put_line(fd, "wincmd w") == FAIL)
return FAIL;
goto fail;
next_arg_idx = wp->w_arg_idx;
}
@@ -839,12 +852,12 @@ makeopens(
// Restore cursor to the current window if it's not the first one.
if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0
|| put_eol(fd) == FAIL))
return FAIL;
goto fail;
// Restore window sizes again after jumping around in windows, because
// the current window has a minimum size while others may not.
if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
return FAIL;
goto fail;
// Don't continue in another tab page when doing only the current one
// or when at the last tab page.
@@ -856,10 +869,10 @@ makeopens(
{
if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0
|| put_eol(fd) == FAIL)
return FAIL;
goto fail;
}
if (restore_stal && put_line(fd, "set stal=1") == FAIL)
return FAIL;
goto fail;
// Now put the remaining buffers into the buffer list.
// This is near the end, so that when 'hidden' is set we don't create extra
@@ -880,38 +893,43 @@ makeopens(
if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L
: buf->b_wininfo->wi_fpos.lnum) < 0
|| ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL)
return FAIL;
goto fail;
}
}
// Wipe out an empty unnamed buffer we started in.
if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0")
== FAIL)
return FAIL;
goto fail;
if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL)
return FAIL;
goto fail;
if (put_line(fd, "endif") == FAIL)
return FAIL;
goto fail;
if (put_line(fd, "unlet! s:wipebuf") == FAIL)
return FAIL;
goto fail;
// Re-apply 'winheight', 'winwidth' and 'shortmess'.
if (fprintf(fd, "set winheight=%ld winwidth=%ld shortmess=%s",
p_wh, p_wiw, p_shm) < 0 || put_eol(fd) == FAIL)
return FAIL;
goto fail;
// Re-apply 'winminheight' and 'winminwidth'.
if (fprintf(fd, "set winminheight=%ld winminwidth=%ld",
p_wmh, p_wmw) < 0 || put_eol(fd) == FAIL)
return FAIL;
goto fail;
// Lastly, execute the x.vim file if it exists.
if (put_line(fd, "let s:sx = expand(\"<sfile>:p:r\").\"x.vim\"") == FAIL
|| put_line(fd, "if filereadable(s:sx)") == FAIL
|| put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL
|| put_line(fd, "endif") == FAIL)
return FAIL;
goto fail;
return OK;
ret = OK;
fail:
#ifdef FEAT_TERMINAL
hash_clear_all(&terminal_bufs, 0);
#endif
return ret;
}
/*
@@ -1248,8 +1266,8 @@ ex_mkrc(exarg_T *eap)
}
else
{
failed |= (put_view(fd, curwin, !using_vdir, flagp,
-1) == FAIL);
failed |= (put_view(fd, curwin, !using_vdir, flagp, -1, NULL)
== FAIL);
}
if (put_line(fd, "let &so = s:so_save | let &siso = s:siso_save")
== FAIL)
-4
View File
@@ -66,10 +66,6 @@
#define REGION_ALL 0xff // word valid in all regions
#define VIMSUGMAGIC "VIMsug" // string at start of Vim .sug file
#define VIMSUGMAGICL 6
#define VIMSUGVERSION 1
// Result values. Lower number is accepted over higher one.
#define SP_BANNED -1
#define SP_OK 0
+1
View File
@@ -1374,6 +1374,7 @@ struct type_S {
#define TTFLAG_VARARGS 1 // func args ends with "..."
#define TTFLAG_OPTARG 2 // func arg type with "?"
#define TTFLAG_BOOL_OK 4 // can be converted to bool
#define TTFLAG_STATIC 8 // one of the static types, e.g. t_any
/*
* Structure to hold an internal variable without a name.
+2
View File
@@ -6316,9 +6316,11 @@ ex_ownsyntax(exarg_T *eap)
#ifdef FEAT_SPELL
// TODO: keep the spell checking as it was.
curwin->w_p_spell = FALSE; // No spell checking
// make sure option values are "empty_option" instead of NULL
clear_string_option(&curwin->w_s->b_p_spc);
clear_string_option(&curwin->w_s->b_p_spf);
clear_string_option(&curwin->w_s->b_p_spl);
clear_string_option(&curwin->w_s->b_p_spo);
#endif
clear_string_option(&curwin->w_s->b_syn_isk);
}
+36 -2
View File
@@ -935,9 +935,30 @@ theend:
* Return FAIL if writing fails.
*/
int
term_write_session(FILE *fd, win_T *wp)
term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs)
{
term_T *term = wp->w_buffer->b_term;
const int bufnr = wp->w_buffer->b_fnum;
term_T *term = wp->w_buffer->b_term;
if (terminal_bufs != NULL && wp->w_buffer->b_nwindows > 1)
{
// There are multiple views into this terminal buffer. We don't want to
// create the terminal multiple times. If it's the first time, create,
// otherwise link to the first buffer.
char id_as_str[NUMBUFLEN];
hashitem_T *entry;
vim_snprintf(id_as_str, sizeof(id_as_str), "%d", bufnr);
entry = hash_find(terminal_bufs, (char_u *)id_as_str);
if (!HASHITEM_EMPTY(entry))
{
// we've already opened this terminal buffer
if (fprintf(fd, "execute 'buffer ' . s:term_buf_%d", bufnr) < 0)
return FAIL;
return put_eol(fd);
}
}
// Create the terminal and run the command. This is not without
// risk, but let's assume the user only creates a session when this
@@ -951,6 +972,19 @@ term_write_session(FILE *fd, win_T *wp)
#endif
if (term->tl_command != NULL && fputs((char *)term->tl_command, fd) < 0)
return FAIL;
if (put_eol(fd) != OK)
return FAIL;
if (fprintf(fd, "let s:term_buf_%d = bufnr()", bufnr) < 0)
return FAIL;
if (terminal_bufs != NULL && wp->w_buffer->b_nwindows > 1)
{
char *hash_key = alloc(NUMBUFLEN);
vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr);
hash_add(terminal_bufs, (char_u *)hash_key);
}
return put_eol(fd);
}
+1 -1
View File
@@ -95,7 +95,7 @@ tinytests: $(SCRIPTS_TINY_OUT)
# Copy the input files to dostmp, changing the fileformat to dos.
$(DOSTMP)/%.in : %.in
if not exist $(DOSTMP)\nul mkdir $(DOSTMP)
if not exist $@ $(DEL) $@
if exist $(DOSTMP)\$< $(DEL) $(DOSTMP)\$<
$(VIMPROG) -u dos.vim $(NO_INITS) "+set ff=dos|f $@|wq" $<
# For each input file dostmp/test99.in run the tests.
+10
View File
@@ -0,0 +1,10 @@
>T+0&#ffffff0|h|e| |q|u|i|c|k| |b|r|o|w|n| |f|o|x| |j|u|m|p|e|d| |o|v|e|r| |t|h|e| @3| +0&#ffd7d7255
@1| +0&#ffffff0|l+0&#ffd7d7255|a+0&#ffffff0|z|y| |d|o|g|s| @28
|~+0#4040ff13&| @38
|~| @38
|~| @38
|~| @38
|~| @38
|~| @38
|~| @38
| +0#0000000&@21|1|,|1| @10|A|l@1|
+10
View File
@@ -0,0 +1,10 @@
>T+0&#ffffff0|h|e| |q|u|i|c|k| |b|r|o|w|n| |f|o|x| |j|u|m|p|e|d| |o|v|e|r| |t|h|e| |l|a|z|y+0&#ffd7d7255
|++0#4040ff13&|++0&#ffffff0|++0&#ffd7d7255|>+0&#ffffff0| | +0#0000000&|d|o|g|s| @29
|~+0#4040ff13&| @38
|~| @38
|~| @38
|~| @38
|~| @38
|~| @38
|~| @38
| +0#0000000&@21|1|,|1| @10|A|l@1|
+5 -5
View File
@@ -254,35 +254,35 @@ func Test_assert_fail_fails()
catch
let exp = v:exception
endtry
call assert_match("E856: assert_fails() second argument", exp)
call assert_match("E856: \"assert_fails()\" second argument", exp)
try
call assert_equal(1, assert_fails('xxx', ['1', '2', '3']))
catch
let exp = v:exception
endtry
call assert_match("E856: assert_fails() second argument", exp)
call assert_match("E856: \"assert_fails()\" second argument", exp)
try
call assert_equal(1, assert_fails('xxx', #{one: 1}))
catch
let exp = v:exception
endtry
call assert_match("E856: assert_fails() second argument", exp)
call assert_match("E856: \"assert_fails()\" second argument", exp)
try
call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp'))
catch
let exp = v:exception
endtry
call assert_match("E1115: assert_fails() fourth argument must be a number", exp)
call assert_match("E1115: \"assert_fails()\" fourth argument must be a number", exp)
try
call assert_equal(1, assert_fails('xxx', 'E492', '', 54, 123))
catch
let exp = v:exception
endtry
call assert_match("E1116: assert_fails() fifth argument must be a string", exp)
call assert_match("E1116: \"assert_fails()\" fifth argument must be a string", exp)
endfunc
func Test_assert_fails_in_try_block()
+8
View File
@@ -215,6 +215,14 @@ func Test_lockvar()
if 0 | lockvar x | endif
let x = 'again'
let val = [1, 2, 3]
lockvar 0 val
let val[0] = 9
call assert_equal([9, 2, 3], val)
call add(val, 4)
call assert_equal([9, 2, 3, 4], val)
call assert_fails('let val = [4, 5, 6]', 'E1122:')
endfunc
+5 -4
View File
@@ -39,9 +39,9 @@ endfunc
func Test_expand_sfile_and_stack()
call assert_match('test_expand_func\.vim$', s:sfile)
let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$'
call assert_match(expected , expand('<sfile>'))
call assert_match(expected , expand('<stack>'))
let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack'
call assert_match(expected .. '$', expand('<sfile>'))
call assert_match(expected .. '\[4\]' , expand('<stack>'))
" Call in script-local function
call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack\[7\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
@@ -53,11 +53,12 @@ func Test_expand_sfile_and_stack()
" Use <stack> from sourced script.
let lines =<< trim END
" comment here
let g:stack_value = expand('<stack>')
END
call writefile(lines, 'Xstack')
source Xstack
call assert_match('\<Xstack$', g:stack_value)
call assert_match('\<Xstack\[2\]$', g:stack_value)
call delete('Xstack')
endfunc
+24
View File
@@ -2554,4 +2554,28 @@ func Test_browsedir()
call assert_fails('call browsedir("open", [])', 'E730:')
endfunc
" Test for matchfuzzy()
func Test_matchfuzzy()
call assert_fails('call matchfuzzy(10, "abc")', 'E714:')
call assert_fails('call matchfuzzy(["abc"], [])', 'E730:')
call assert_equal([], matchfuzzy([], 'abc'))
call assert_equal([], matchfuzzy(['abc'], ''))
call assert_equal(['abc'], matchfuzzy(['abc', 10], 'ac'))
call assert_equal([], matchfuzzy([10, 20], 'ac'))
call assert_equal(['abc'], matchfuzzy(['abc'], 'abc'))
call assert_equal(['crayon', 'camera'], matchfuzzy(['camera', 'crayon'], 'cra'))
call assert_equal(['aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa', 'aba'], matchfuzzy(['aba', 'aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa'], 'aa'))
call assert_equal(['one'], matchfuzzy(['one', 'two'], 'one'))
call assert_equal(['oneTwo', 'onetwo'], matchfuzzy(['onetwo', 'oneTwo'], 'oneTwo'))
call assert_equal(['one_two', 'onetwo'], matchfuzzy(['onetwo', 'one_two'], 'oneTwo'))
call assert_equal(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], matchfuzzy(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], 'aa'))
call assert_equal([], matchfuzzy([repeat('a', 300)], repeat('a', 257)))
%bw!
eval ['somebuf', 'anotherone', 'needle', 'yetanotherone']->map({_, v -> bufadd(v) + bufload(v)})
let l = getbufinfo()->map({_, v -> v.name})->matchfuzzy('ndl')
call assert_equal(1, len(l))
call assert_match('needle', l[0])
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+20 -1
View File
@@ -124,7 +124,7 @@ func Test_gf()
endfunc
func Test_gf_visual()
call writefile([], "Xtest_gf_visual")
call writefile(['one', 'two', 'three', 'four'], "Xtest_gf_visual")
new
call setline(1, 'XXXtest_gf_visualXXX')
set hidden
@@ -138,6 +138,25 @@ func Test_gf_visual()
normal VGgf
call assert_equal('Xtest_gf_visual', @%)
" following line number is used for gF
bwipe!
new
call setline(1, 'XXXtest_gf_visual:3XXX')
norm! 0ttvt:gF
call assert_equal('Xtest_gf_visual', bufname('%'))
call assert_equal(3, getcurpos()[1])
" line number in visual area is used for file name
if has('unix')
bwipe!
call writefile([], "Xtest_gf_visual:3")
new
call setline(1, 'XXXtest_gf_visual:3XXX')
norm! 0ttvtXgF
call assert_equal('Xtest_gf_visual:3', bufname('%'))
call delete('Xtest_gf_visual:3')
endif
bwipe!
call delete('Xtest_gf_visual')
set hidden&
+68
View File
@@ -662,6 +662,42 @@ func Test_colorcolumn()
call delete('Xtest_colorcolumn')
endfunc
func Test_colorcolumn_bri()
CheckScreendump
" check 'colorcolumn' when 'breakindent' is set
let lines =<< trim END
call setline(1, 'The quick brown fox jumped over the lazy dogs')
END
call writefile(lines, 'Xtest_colorcolumn_bri')
let buf = RunVimInTerminal('-S Xtest_colorcolumn_bri', {'rows': 10,'columns': 40})
call term_sendkeys(buf, ":set co=40 linebreak bri briopt=shift:2 cc=40,41,43\<CR>")
call TermWait(buf)
call VerifyScreenDump(buf, 'Test_colorcolumn_2', {})
" clean up
call StopVimInTerminal(buf)
call delete('Xtest_colorcolumn_bri')
endfunc
func Test_colorcolumn_sbr()
CheckScreendump
" check 'colorcolumn' when 'showbreak' is set
let lines =<< trim END
call setline(1, 'The quick brown fox jumped over the lazy dogs')
END
call writefile(lines, 'Xtest_colorcolumn_srb')
let buf = RunVimInTerminal('-S Xtest_colorcolumn_srb', {'rows': 10,'columns': 40})
call term_sendkeys(buf, ":set co=40 showbreak=+++>\\ cc=40,41,43\<CR>")
call TermWait(buf)
call VerifyScreenDump(buf, 'Test_colorcolumn_3', {})
" clean up
call StopVimInTerminal(buf)
call delete('Xtest_colorcolumn_srb')
endfunc
" This test must come before the Test_cursorline test, as it appears this
" defines the Normal highlighting group anyway.
func Test_1_highlight_Normalgroup_exists()
@@ -796,4 +832,36 @@ func Test_highlight_term_attr()
hi clear
endfunc
" Test default highlighting is restored
func Test_highlight_restore_defaults()
hi! link TestLink Identifier
hi! TestHi ctermbg=red
let hlTestLinkPre = HighlightArgs('TestLink')
let hlTestHiPre = HighlightArgs('TestHi')
" Test colorscheme
hi clear
if exists('syntax_on')
syntax reset
endif
let g:colors_name = 'test'
hi! link TestLink ErrorMsg
hi! TestHi ctermbg=green
" Restore default highlighting
colorscheme default
syntax on
" 'default' should work no matter if highlight group was cleared
hi def link TestLink Identifier
hi def TestHi ctermbg=red
let hlTestLinkPost = HighlightArgs('TestLink')
let hlTestHiPost = HighlightArgs('TestHi')
call assert_equal(hlTestLinkPre, hlTestLinkPost)
call assert_equal(hlTestHiPre, hlTestHiPost)
hi clear
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+18
View File
@@ -313,6 +313,24 @@ func Test_CompleteDone_undo()
au! CompleteDone
endfunc
func CompleteTest(findstart, query)
if a:findstart
return col('.')
endif
return ['matched']
endfunc
func Test_completefunc_info()
new
set completeopt=menuone
set completefunc=CompleteTest
call feedkeys("i\<C-X>\<C-U>\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
call assert_equal("matched{'pum_visible': 1, 'mode': 'function', 'selected': -1, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1))
bwipe!
set completeopt&
set completefunc&
endfunc
" Check that when using feedkeys() typeahead does not interrupt searching for
" completions.
func Test_compl_feedkeys()
+8 -5
View File
@@ -354,7 +354,7 @@ endfunc
" Locked variables
func Test_list_locked_var()
let expected = [
\ [['0000-000', 'ppppppp'],
\ [['1000-000', 'ppppppF'],
\ ['0000-000', 'ppppppp'],
\ ['0000-000', 'ppppppp']],
\ [['1000-000', 'ppppppF'],
@@ -381,7 +381,7 @@ func Test_list_locked_var()
exe "unlockvar " . depth . " l"
endif
let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]")
call assert_equal(expected[depth][u][0], ps)
call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth)
let ps = ''
try
let l[1][1][0] = 99
@@ -425,7 +425,7 @@ func Test_list_locked_var()
catch
let ps .= 'F'
endtry
call assert_equal(expected[depth][u][1], ps)
call assert_equal(expected[depth][u][1], ps, 'depth: ' .. depth)
endfor
endfor
call assert_fails("let x=islocked('a b')", 'E488:')
@@ -438,7 +438,7 @@ endfunc
" Unletting locked variables
func Test_list_locked_var_unlet()
let expected = [
\ [['0000-000', 'ppppppp'],
\ [['1000-000', 'ppppppp'],
\ ['0000-000', 'ppppppp'],
\ ['0000-000', 'ppppppp']],
\ [['1000-000', 'ppFppFp'],
@@ -466,7 +466,7 @@ func Test_list_locked_var_unlet()
exe "unlockvar " . depth . " l"
endif
let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]")
call assert_equal(expected[depth][u][0], ps)
call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth)
let ps = ''
try
unlet l[2]['6'][7]
@@ -666,6 +666,9 @@ func Test_func_arg_list()
call s:arg_list_test(1, 2, [3, 4], {5: 6})
endfunc
func Test_dict_item_locked()
endfunc
" Tests for reverse(), sort(), uniq()
func Test_reverse_sort_uniq()
let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5]
+50 -3
View File
@@ -351,9 +351,8 @@ func Test_mksession_blank_windows()
call delete('Xtest_mks.out')
endfunc
if has('terminal')
func Test_mksession_terminal_shell()
CheckFeature terminal
CheckFeature quickfix
terminal
@@ -374,6 +373,8 @@ func Test_mksession_terminal_shell()
endfunc
func Test_mksession_terminal_no_restore_cmdarg()
CheckFeature terminal
terminal ++norestore
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
@@ -389,6 +390,8 @@ func Test_mksession_terminal_no_restore_cmdarg()
endfunc
func Test_mksession_terminal_no_restore_funcarg()
CheckFeature terminal
call term_start(&shell, {'norestore': 1})
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
@@ -404,6 +407,8 @@ func Test_mksession_terminal_no_restore_funcarg()
endfunc
func Test_mksession_terminal_no_restore_func()
CheckFeature terminal
terminal
call term_setrestore(bufnr('%'), 'NONE')
mksession! Xtest_mks.out
@@ -420,6 +425,8 @@ func Test_mksession_terminal_no_restore_func()
endfunc
func Test_mksession_terminal_no_ssop()
CheckFeature terminal
terminal
set sessionoptions-=terminal
mksession! Xtest_mks.out
@@ -437,6 +444,7 @@ func Test_mksession_terminal_no_ssop()
endfunc
func Test_mksession_terminal_restore_other()
CheckFeature terminal
CheckFeature quickfix
terminal
@@ -455,7 +463,46 @@ func Test_mksession_terminal_restore_other()
call delete('Xtest_mks.out')
endfunc
endif " has('terminal')
func Test_mksession_terminal_shared_windows()
CheckFeature terminal
terminal
let term_buf = bufnr()
new
execute "buffer" term_buf
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
let found_creation = 0
let found_use = 0
for line in lines
if line =~ '^terminal'
let found_creation = 1
call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+', line)
elseif line =~ "^execute 'buffer ' . s:term_buf_" . term_buf . "$"
let found_use = 1
endif
endfor
call assert_true(found_creation && found_use)
call StopShellInTerminal(term_buf)
call delete('Xtest_mks.out')
endfunc
func Test_mkview_terminal_windows()
CheckFeature terminal
" create two window on the same terminal to check this is handled OK
terminal
let term_buf = bufnr()
exe 'sbuf ' .. term_buf
mkview! Xtestview
call StopShellInTerminal(term_buf)
call delete('Xtestview')
endfunc
" Test :mkview with a file argument.
func Test_mkview_file()
+30
View File
@@ -1,5 +1,6 @@
" Test for options
source shared.vim
source check.vim
source view_util.vim
@@ -587,6 +588,35 @@ func Test_backupskip()
endif
endfor
" Duplicates from environment variables should be filtered out (option has
" P_NODUP). Run this in a separate instance and write v:errors in a file,
" so that we see what happens on startup.
let after =<< trim [CODE]
let bsklist = split(&backupskip, ',')
call assert_equal(uniq(copy(bsklist)), bsklist)
call writefile(['errors:'] + v:errors, 'Xtestout')
qall
[CODE]
call writefile(after, 'Xafter')
let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"'
let saveenv = {}
for var in ['TMPDIR', 'TMP', 'TEMP']
let saveenv[var] = getenv(var)
call setenv(var, '/duplicate/path')
endfor
exe 'silent !' . cmd
call assert_equal(['errors:'], readfile('Xtestout'))
" restore environment variables
for var in ['TMPDIR', 'TMP', 'TEMP']
call setenv(var, saveenv[var])
endfor
call delete('Xtestout')
call delete('Xafter')
" Duplicates should be filtered out (option has P_NODUP)
let backupskip = &backupskip
set backupskip=
+24
View File
@@ -1430,6 +1430,30 @@ func Test_quickfix_was_changed_by_autocmd()
call XquickfixChangedByAutocmd('l')
endfunc
func Test_setloclist_in_autocommand()
call writefile(['test1', 'test2'], 'Xfile')
edit Xfile
let s:bufnr = bufnr()
call setloclist(1,
\ [{'bufnr' : s:bufnr, 'lnum' : 1, 'text' : 'test1'},
\ {'bufnr' : s:bufnr, 'lnum' : 2, 'text' : 'test2'}])
augroup Test_LocList
au!
autocmd BufEnter * call setloclist(1,
\ [{'bufnr' : s:bufnr, 'lnum' : 1, 'text' : 'test1'},
\ {'bufnr' : s:bufnr, 'lnum' : 2, 'text' : 'test2'}], 'r')
augroup END
lopen
call assert_fails('exe "normal j\<CR>"', 'E926:')
augroup Test_LocList
au!
augroup END
call delete('Xfile')
endfunc
func Test_caddbuffer_to_empty()
helpgr quickfix
call setqflist([], 'r')
+1
View File
@@ -112,6 +112,7 @@ foobar/?
set spelllang=
call assert_fails("call spellbadword('maxch')", 'E756:')
call assert_fails("spelldump", 'E756:')
call delete('Xwords.spl')
call delete('Xwords')
+31
View File
@@ -307,6 +307,9 @@ func Test_spellfile_format_error()
" SN_SOFO: empty sofofrom and sofoto
call Spellfile_Test(0z06000000000400000000FF000000000000000000000000, '')
" SN_SOFO: multi-byte characters in sofofrom and sofoto
call Spellfile_Test(0z0600000000080002CF810002CF82FF000000000000000000000000, '')
" SN_COMPOUND: compmax is less than 2
call Spellfile_Test(0z08000000000101, 'E759:')
@@ -550,8 +553,14 @@ endfunc
" Test for the :mkspell command
func Test_mkspell()
call assert_fails('mkspell Xtest_us.spl', 'E751:')
call assert_fails('mkspell Xtest.spl abc', 'E484:')
call assert_fails('mkspell a b c d e f g h i j k', 'E754:')
" create a .aff file but not the .dic file
call writefile([], 'Xtest.aff')
call assert_fails('mkspell Xtest.spl Xtest', 'E484:')
call delete('Xtest.aff')
call writefile([], 'Xtest.spl')
call writefile([], 'Xtest.dic')
call assert_fails('mkspell Xtest.spl Xtest.dic', 'E13:')
@@ -772,6 +781,14 @@ func Test_aff_file_format_error()
call assert_fails('mkspell! Xtest.spl Xtest', 'E761:')
let &encoding = save_encoding
" missing UPP entry
call writefile(["FOL abc", "LOW abc"], 'Xtest.aff')
let save_encoding = &encoding
set encoding=cp949
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Missing FOL/LOW/UPP line in Xtest.aff', output)
let &encoding = save_encoding
" duplicate word in the .dic file
call writefile(['2', 'good', 'good', 'good'], 'Xtest.dic')
call writefile(['NAME vim'], 'Xtest.aff')
@@ -779,6 +796,20 @@ func Test_aff_file_format_error()
call assert_match('First duplicate word in Xtest.dic line 3: good', output)
call assert_match('2 duplicate word(s) in Xtest.dic', output)
" use multiple .aff files with different values for COMPOUNDWORDMAX and
" MIDWORD (number and string)
call writefile(['1', 'world'], 'Xtest_US.dic')
call writefile(['1', 'world'], 'Xtest_CA.dic')
call writefile(["COMPOUNDWORDMAX 3", "MIDWORD '-"], 'Xtest_US.aff')
call writefile(["COMPOUNDWORDMAX 4", "MIDWORD '="], 'Xtest_CA.aff')
let output = execute('mkspell! Xtest.spl Xtest_US Xtest_CA')
call assert_match('COMPOUNDWORDMAX value differs from what is used in another .aff file', output)
call assert_match('MIDWORD value differs from what is used in another .aff file', output)
call delete('Xtest_US.dic')
call delete('Xtest_CA.dic')
call delete('Xtest_US.aff')
call delete('Xtest_CA.aff')
call delete('Xtest.dic')
call delete('Xtest.aff')
call delete('Xtest.spl')
+4
View File
@@ -428,7 +428,11 @@ func Test_ownsyntax()
call setline(1, '#define FOO')
syntax on
set filetype=c
ownsyntax perl
" this should not crash
set
call assert_equal('perlComment', synIDattr(synID(line('.'), col('.'), 1), 'name'))
call assert_equal('c', b:current_syntax)
call assert_equal('perl', w:current_syntax)
+52 -1
View File
@@ -1273,7 +1273,7 @@ func Test_prop_func_invalid_args()
call assert_fails("call prop_type_list([])", 'E715:')
endfunc
func Test_split_join()
func Test_prop_split_join()
new
call prop_type_add('test', {'highlight': 'ErrorMsg'})
call setline(1, 'just some text')
@@ -1294,4 +1294,55 @@ func Test_split_join()
call prop_type_delete('test')
endfunc
func Test_prop_increment_decrement()
new
call prop_type_add('test', {'highlight': 'ErrorMsg'})
call setline(1, 'its 998 times')
call prop_add(1, 5, {'length': 3, 'type': 'test'})
exe "normal! 0f9\<C-A>"
eval getline(1)->assert_equal('its 999 times')
eval prop_list(1)->assert_equal([
\ #{id: 0, col: 5, end: 1, type: 'test', length: 3, start: 1}])
exe "normal! 0f9\<C-A>"
eval getline(1)->assert_equal('its 1000 times')
eval prop_list(1)->assert_equal([
\ #{id: 0, col: 5, end: 1, type: 'test', length: 4, start: 1}])
bwipe!
call prop_type_delete('test')
endfunc
func Test_prop_block_insert()
new
call prop_type_add('test', {'highlight': 'ErrorMsg'})
call setline(1, ['one ', 'two '])
call prop_add(1, 1, {'length': 3, 'type': 'test'})
call prop_add(2, 1, {'length': 3, 'type': 'test'})
" insert "xx" in the first column of both lines
exe "normal! gg0\<C-V>jIxx\<Esc>"
eval getline(1, 2)->assert_equal(['xxone ', 'xxtwo '])
let expected = [#{id: 0, col: 3, end: 1, type: 'test', length: 3, start: 1}]
eval prop_list(1)->assert_equal(expected)
eval prop_list(2)->assert_equal(expected)
" insert "yy" inside the text props to make them longer
exe "normal! gg03l\<C-V>jIyy\<Esc>"
eval getline(1, 2)->assert_equal(['xxoyyne ', 'xxtyywo '])
let expected[0].length = 5
eval prop_list(1)->assert_equal(expected)
eval prop_list(2)->assert_equal(expected)
" insert "zz" after the text props, text props don't change
exe "normal! gg07l\<C-V>jIzz\<Esc>"
eval getline(1, 2)->assert_equal(['xxoyynezz ', 'xxtyywozz '])
eval prop_list(1)->assert_equal(expected)
eval prop_list(2)->assert_equal(expected)
bwipe!
call prop_type_delete('test')
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+11
View File
@@ -330,6 +330,17 @@ def Test_put_command()
bwipe!
enddef
def Test_command_star_range()
new
setline(1, ['xxx foo xxx', 'xxx bar xxx', 'xxx foo xx bar'])
setpos("'<", [0, 1, 0, 0])
setpos("'>", [0, 3, 0, 0])
:*s/\(foo\|bar\)/baz/g
getline(1, 3)->assert_equal(['xxx baz xxx', 'xxx baz xxx', 'xxx baz xx baz'])
bwipe!
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+3 -3
View File
@@ -258,7 +258,7 @@ def Test_disassemble_list_assign()
'\d STORE $2\_s*' ..
'\[x, y; l\] = g:stringlist\_s*' ..
'\d LOADG g:stringlist\_s*' ..
'\d CHECKTYPE list stack\[-1\]\_s*' ..
'\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
'\d CHECKLEN >= 2\_s*' ..
'\d\+ ITEM 0\_s*' ..
'\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
@@ -829,7 +829,7 @@ def Test_disassemble_for_loop_eval()
'\d STORE -1 in $1\_s*' ..
'\d PUSHS "\["one", "two"\]"\_s*' ..
'\d BCALL eval(argc 1)\_s*' ..
'\d CHECKTYPE list stack\[-1\]\_s*' ..
'\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
'\d FOR $1 -> \d\+\_s*' ..
'\d STORE $2\_s*' ..
'res ..= str\_s*' ..
@@ -1144,7 +1144,7 @@ def Test_disassemble_any_slice()
'\d STORE $0\_s*' ..
'return res\_s*' ..
'\d LOAD $0\_s*' ..
'\d CHECKTYPE list stack\[-1\]\_s*' ..
'\d CHECKTYPE list<number> stack\[-1\]\_s*' ..
'\d RETURN',
instr)
assert_equal([2, 3, 4], AnySlice())
+45 -28
View File
@@ -143,12 +143,12 @@ enddef
func Test_expr1_fails()
call CheckDefFailure(["let x = 1 ? 'one'"], "Missing ':' after '?'", 1)
let msg = "white space required before and after '?'"
let msg = "White space required before and after '?'"
call CheckDefFailure(["let x = 1? 'one' : 'two'"], msg, 1)
call CheckDefFailure(["let x = 1 ?'one' : 'two'"], msg, 1)
call CheckDefFailure(["let x = 1?'one' : 'two'"], msg, 1)
let msg = "white space required before and after ':'"
let msg = "White space required before and after ':'"
call CheckDefFailure(["let x = 1 ? 'one': 'two'"], msg, 1)
call CheckDefFailure(["let x = 1 ? 'one' :'two'"], msg, 1)
call CheckDefFailure(["let x = 1 ? 'one':'two'"], msg, 1)
@@ -276,7 +276,7 @@ def Test_expr2_vimscript()
enddef
func Test_expr2_fails()
let msg = "white space required before and after '||'"
let msg = "White space required before and after '||'"
call CheckDefFailure(["let x = 1||2"], msg, 1)
call CheckDefFailure(["let x = 1 ||2"], msg, 1)
call CheckDefFailure(["let x = 1|| 2"], msg, 1)
@@ -401,7 +401,7 @@ def Test_expr3_vimscript()
enddef
func Test_expr3_fails()
let msg = "white space required before and after '&&'"
let msg = "White space required before and after '&&'"
call CheckDefFailure(["let x = 1&&2"], msg, 1)
call CheckDefFailure(["let x = 1 &&2"], msg, 1)
call CheckDefFailure(["let x = 1&& 2"], msg, 1)
@@ -481,6 +481,7 @@ def Test_expr4_equal()
set noignorecase
CheckDefFailure(["let x = 'a' == xxx"], 'E1001:', 1)
CheckDefExecFailure(['let items: any', 'eval 1', 'eval 2', 'if items == []', 'endif'], 'E691:', 4)
let bb = 0z3f
assert_equal(true, 0z3f == bb)
@@ -860,22 +861,22 @@ def Test_expr4_vim9script()
enddef
func Test_expr4_fails()
let msg = "white space required before and after '>'"
let msg = "White space required before and after '>'"
call CheckDefFailure(["let x = 1>2"], msg, 1)
call CheckDefFailure(["let x = 1 >2"], msg, 1)
call CheckDefFailure(["let x = 1> 2"], msg, 1)
let msg = "white space required before and after '=='"
let msg = "White space required before and after '=='"
call CheckDefFailure(["let x = 1==2"], msg, 1)
call CheckDefFailure(["let x = 1 ==2"], msg, 1)
call CheckDefFailure(["let x = 1== 2"], msg, 1)
let msg = "white space required before and after 'is'"
let msg = "White space required before and after 'is'"
call CheckDefFailure(["let x = '1'is'2'"], msg, 1)
call CheckDefFailure(["let x = '1' is'2'"], msg, 1)
call CheckDefFailure(["let x = '1'is '2'"], msg, 1)
let msg = "white space required before and after 'isnot'"
let msg = "White space required before and after 'isnot'"
call CheckDefFailure(["let x = '1'isnot'2'"], msg, 1)
call CheckDefFailure(["let x = '1' isnot'2'"], msg, 1)
call CheckDefFailure(["let x = '1'isnot '2'"], msg, 1)
@@ -1150,17 +1151,17 @@ def Test_expr5_float()
enddef
func Test_expr5_fails()
let msg = "white space required before and after '+'"
let msg = "White space required before and after '+'"
call CheckDefFailure(["let x = 1+2"], msg, 1)
call CheckDefFailure(["let x = 1 +2"], msg, 1)
call CheckDefFailure(["let x = 1+ 2"], msg, 1)
let msg = "white space required before and after '-'"
let msg = "White space required before and after '-'"
call CheckDefFailure(["let x = 1-2"], msg, 1)
call CheckDefFailure(["let x = 1 -2"], msg, 1)
call CheckDefFailure(["let x = 1- 2"], msg, 1)
let msg = "white space required before and after '..'"
let msg = "White space required before and after '..'"
call CheckDefFailure(["let x = '1'..'2'"], msg, 1)
call CheckDefFailure(["let x = '1' ..'2'"], msg, 1)
call CheckDefFailure(["let x = '1'.. '2'"], msg, 1)
@@ -1305,17 +1306,17 @@ def Test_expr6_float()
enddef
func Test_expr6_fails()
let msg = "white space required before and after '*'"
let msg = "White space required before and after '*'"
call CheckDefFailure(["let x = 1*2"], msg, 1)
call CheckDefFailure(["let x = 1 *2"], msg, 1)
call CheckDefFailure(["let x = 1* 2"], msg, 1)
let msg = "white space required before and after '/'"
let msg = "White space required before and after '/'"
call CheckDefFailure(["let x = 1/2"], msg, 1)
call CheckDefFailure(["let x = 1 /2"], msg, 1)
call CheckDefFailure(["let x = 1/ 2"], msg, 1)
let msg = "white space required before and after '%'"
let msg = "White space required before and after '%'"
call CheckDefFailure(["let x = 1%2"], msg, 1)
call CheckDefFailure(["let x = 1 %2"], msg, 1)
call CheckDefFailure(["let x = 1% 2"], msg, 1)
@@ -1437,8 +1438,11 @@ def Test_expr7_vimvar()
let old: list<string> = v:oldfiles
let compl: dict<any> = v:completed_item
CheckDefFailure(["let old: list<number> = v:oldfiles"], 'E1012: type mismatch, expected list<number> but got list<string>', 1)
CheckDefFailure(["let old: dict<number> = v:completed_item"], 'E1012: type mismatch, expected dict<number> but got dict<any>', 1)
CheckDefFailure(["let old: list<number> = v:oldfiles"], 'E1012: Type mismatch; expected list<number> but got list<string>', 1)
new
exec "normal! afoo fo\<C-N>\<Esc>"
CheckDefExecFailure(["let old: dict<number> = v:completed_item"], 'E1012: Type mismatch; expected dict<number> but got dict<string>', 1)
bwipe!
enddef
def Test_expr7_special()
@@ -1519,14 +1523,14 @@ def Test_expr7_list()
CheckDefExecFailure(["echo 1", "let x = [][0]", "echo 3"], 'E684:', 2)
CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1029:', 1)
CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1012:', 1)
CheckDefFailure(["let x = g:list_mixed["], 'E1097:', 2)
CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:', 2)
CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:', 1)
CheckDefFailure(["let l: list<number> = [234, 'x']"], 'E1012:', 1)
CheckDefFailure(["let l: list<number> = ['x', 234]"], 'E1012:', 1)
CheckDefFailure(["let l: list<string> = [234, 'x']"], 'E1012:', 1)
CheckDefFailure(["let l: list<string> = ['x', 123]"], 'E1012:', 1)
CheckDefExecFailure(["let l: list<number> = [234, 'x']"], 'E1012:', 1)
CheckDefExecFailure(["let l: list<number> = ['x', 234]"], 'E1012:', 1)
CheckDefExecFailure(["let l: list<string> = [234, 'x']"], 'E1012:', 1)
CheckDefExecFailure(["let l: list<string> = ['x', 123]"], 'E1012:', 1)
enddef
def Test_expr7_list_vim9script()
@@ -1654,7 +1658,7 @@ def Test_expr7_lambda()
assert_equal('xxxyyy', 'xxx'->{a, b -> a .. b}('yyy'))
CheckDefExecFailure(["let s = 'asdf'->{a -> a}('x')"],
'E1106: one argument too many')
'E1106: One argument too many')
CheckDefExecFailure(["let s = 'asdf'->{a -> a}('x', 'y')"],
'E1106: 2 arguments too many')
CheckDefFailure(["echo 'asdf'->{a -> a}(x)"], 'E1001:', 1)
@@ -1730,10 +1734,10 @@ def Test_expr7_dict()
CheckDefExecFailure(["let x = g:anint.member"], 'E715:', 1)
CheckDefExecFailure(["let x = g:dict_empty.member"], 'E716:', 1)
CheckDefFailure(['let x: dict<number> = #{a: 234, b: "1"}'], 'E1012:', 1)
CheckDefFailure(['let x: dict<number> = #{a: "x", b: 134}'], 'E1012:', 1)
CheckDefFailure(['let x: dict<string> = #{a: 234, b: "1"}'], 'E1012:', 1)
CheckDefFailure(['let x: dict<string> = #{a: "x", b: 134}'], 'E1012:', 1)
CheckDefExecFailure(['let x: dict<number> = #{a: 234, b: "1"}'], 'E1012:', 1)
CheckDefExecFailure(['let x: dict<number> = #{a: "x", b: 134}'], 'E1012:', 1)
CheckDefExecFailure(['let x: dict<string> = #{a: 234, b: "1"}'], 'E1012:', 1)
CheckDefExecFailure(['let x: dict<string> = #{a: "x", b: 134}'], 'E1012:', 1)
enddef
def Test_expr7_dict_vim9script()
@@ -1839,7 +1843,7 @@ def Test_expr_member()
CheckDefFailure(["let x = g:dict_one.#$!"], 'E1002:', 1)
CheckDefExecFailure(["let d: dict<any>", "echo d['a']"], 'E716:', 2)
CheckDefExecFailure(["let d: dict<number>", "d = g:list_empty"], 'E1029: Expected dict but got list', 2)
CheckDefExecFailure(["let d: dict<number>", "d = g:list_empty"], 'E1012: Type mismatch; expected dict<number> but got list<unknown>', 2)
enddef
def Test_expr7_any_index_slice()
@@ -2310,10 +2314,20 @@ def Test_expr7_list_subscript()
CheckScriptSuccess(['vim9script'] + lines)
lines = ['let l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
CheckDefExecFailure(lines, 'E1029:')
CheckDefExecFailure(lines, 'E1012:')
CheckScriptFailure(['vim9script'] + lines, 'E1030:', 3)
enddef
def Test_expr7_dict_subscript()
let lines =<< trim END
vim9script
let l = [#{lnum: 2}, #{lnum: 1}]
let res = l[0].lnum > l[1].lnum
assert_true(res)
END
CheckScriptSuccess(lines)
enddef
def Test_expr7_subscript_linebreak()
let range = range(
3)
@@ -2369,6 +2383,9 @@ def Test_expr7_method_call()
type: '',
module: ''}
], getloclist(0))
let result: bool = get(#{n: 0}, 'n', 0)
assert_equal(false, result)
enddef
func Test_expr7_trailing_fails()
File diff suppressed because it is too large Load Diff
+155 -45
View File
@@ -180,9 +180,9 @@ def Test_assignment()
CheckDefFailure(['&notex += 3'], 'E113:')
CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
CheckDefFailure(['&ts = [7]'], 'E1012:')
CheckDefExecFailure(['&ts = g:alist'], 'E1029: Expected number but got list')
CheckDefExecFailure(['&ts = g:alist'], 'E1012: Type mismatch; expected number but got list<number>')
CheckDefFailure(['&ts = "xx"'], 'E1012:')
CheckDefExecFailure(['&ts = g:astring'], 'E1029: Expected number but got string')
CheckDefExecFailure(['&ts = g:astring'], 'E1012: Type mismatch; expected number but got string')
CheckDefFailure(['&path += 3'], 'E1012:')
CheckDefExecFailure(['&bs = "asdf"'], 'E474:')
# test freeing ISN_STOREOPT
@@ -319,7 +319,6 @@ def Test_assignment_list()
list3 += ['end']
assert_equal(['sdf', 'asdf', 'end'], list3)
CheckDefExecFailure(['let ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:')
CheckDefExecFailure(['let [v1, v2] = [1, 2]'], 'E1092:')
@@ -367,7 +366,7 @@ def Test_assignment_dict()
enddef
assert_equal(#{a: 43}, FillDict())
END
call CheckScriptSuccess(lines)
CheckScriptSuccess(lines)
lines =<< trim END
vim9script
@@ -378,7 +377,7 @@ def Test_assignment_dict()
enddef
FillDict()
END
call CheckScriptFailure(lines, 'E1103:')
CheckScriptFailure(lines, 'E1103:')
# assignment to global dict
lines =<< trim END
@@ -390,7 +389,7 @@ def Test_assignment_dict()
enddef
assert_equal(#{a: 43}, FillDict())
END
call CheckScriptSuccess(lines)
CheckScriptSuccess(lines)
# assignment to buffer dict
lines =<< trim END
@@ -402,7 +401,7 @@ def Test_assignment_dict()
enddef
assert_equal(#{a: 43}, FillDict())
END
call CheckScriptSuccess(lines)
CheckScriptSuccess(lines)
enddef
def Test_assignment_local()
@@ -440,7 +439,7 @@ def Test_assignment_local()
enddef
call Test_assignment_local_internal()
END
call CheckScriptSuccess(script_lines)
CheckScriptSuccess(script_lines)
enddef
def Test_assignment_default()
@@ -794,37 +793,102 @@ def Test_delfunction()
CheckScriptSuccess(lines)
enddef
func Test_wrong_type()
call CheckDefFailure(['let var: list<nothing>'], 'E1010:')
call CheckDefFailure(['let var: list<list<nothing>>'], 'E1010:')
call CheckDefFailure(['let var: dict<nothing>'], 'E1010:')
call CheckDefFailure(['let var: dict<dict<nothing>>'], 'E1010:')
def Test_wrong_type()
CheckDefFailure(['let var: list<nothing>'], 'E1010:')
CheckDefFailure(['let var: list<list<nothing>>'], 'E1010:')
CheckDefFailure(['let var: dict<nothing>'], 'E1010:')
CheckDefFailure(['let var: dict<dict<nothing>>'], 'E1010:')
call CheckDefFailure(['let var: dict<number'], 'E1009:')
call CheckDefFailure(['let var: dict<list<number>'], 'E1009:')
CheckDefFailure(['let var: dict<number'], 'E1009:')
CheckDefFailure(['let var: dict<list<number>'], 'E1009:')
call CheckDefFailure(['let var: ally'], 'E1010:')
call CheckDefFailure(['let var: bram'], 'E1010:')
call CheckDefFailure(['let var: cathy'], 'E1010:')
call CheckDefFailure(['let var: dom'], 'E1010:')
call CheckDefFailure(['let var: freddy'], 'E1010:')
call CheckDefFailure(['let var: john'], 'E1010:')
call CheckDefFailure(['let var: larry'], 'E1010:')
call CheckDefFailure(['let var: ned'], 'E1010:')
call CheckDefFailure(['let var: pam'], 'E1010:')
call CheckDefFailure(['let var: sam'], 'E1010:')
call CheckDefFailure(['let var: vim'], 'E1010:')
CheckDefFailure(['let var: ally'], 'E1010:')
CheckDefFailure(['let var: bram'], 'E1010:')
CheckDefFailure(['let var: cathy'], 'E1010:')
CheckDefFailure(['let var: dom'], 'E1010:')
CheckDefFailure(['let var: freddy'], 'E1010:')
CheckDefFailure(['let var: john'], 'E1010:')
CheckDefFailure(['let var: larry'], 'E1010:')
CheckDefFailure(['let var: ned'], 'E1010:')
CheckDefFailure(['let var: pam'], 'E1010:')
CheckDefFailure(['let var: sam'], 'E1010:')
CheckDefFailure(['let var: vim'], 'E1010:')
call CheckDefFailure(['let Ref: number', 'Ref()'], 'E1085:')
call CheckDefFailure(['let Ref: string', 'let res = Ref()'], 'E1085:')
endfunc
CheckDefFailure(['let Ref: number', 'Ref()'], 'E1085:')
CheckDefFailure(['let Ref: string', 'let res = Ref()'], 'E1085:')
enddef
func Test_const()
call CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:')
call CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:')
call CheckDefFailure(['const two'], 'E1021:')
call CheckDefFailure(['const &option'], 'E996:')
endfunc
def Test_const()
CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:')
CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:')
CheckDefFailure(['const list = [1, 2]', 'let list = [3, 4]'], 'E1017:')
CheckDefFailure(['const two'], 'E1021:')
CheckDefFailure(['const &option'], 'E996:')
let lines =<< trim END
const list = [1, 2, 3]
list[0] = 4
list->assert_equal([4, 2, 3])
const! other = [5, 6, 7]
other->assert_equal([5, 6, 7])
let varlist = [7, 8]
const! constlist = [1, varlist, 3]
varlist[0] = 77
# TODO: does not work yet
# constlist[1][1] = 88
let cl = constlist[1]
cl[1] = 88
constlist->assert_equal([1, [77, 88], 3])
let vardict = #{five: 5, six: 6}
const! constdict = #{one: 1, two: vardict, three: 3}
vardict['five'] = 55
# TODO: does not work yet
# constdict['two']['six'] = 66
let cd = constdict['two']
cd['six'] = 66
constdict->assert_equal(#{one: 1, two: #{five: 55, six: 66}, three: 3})
END
CheckDefAndScriptSuccess(lines)
enddef
def Test_const_bang()
let lines =<< trim END
const! var = 234
var = 99
END
CheckDefExecFailure(lines, 'E1018:', 2)
CheckScriptFailure(['vim9script'] + lines, 'E46:', 3)
lines =<< trim END
const! ll = [2, 3, 4]
ll[0] = 99
END
CheckDefExecFailure(lines, 'E1119:', 2)
CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
lines =<< trim END
const! ll = [2, 3, 4]
ll[3] = 99
END
CheckDefExecFailure(lines, 'E1118:', 2)
CheckScriptFailure(['vim9script'] + lines, 'E684:', 3)
lines =<< trim END
const! dd = #{one: 1, two: 2}
dd["one"] = 99
END
CheckDefExecFailure(lines, 'E1121:', 2)
CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
lines =<< trim END
const! dd = #{one: 1, two: 2}
dd["three"] = 99
END
CheckDefExecFailure(lines, 'E1120:')
CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
enddef
def Test_range_no_colon()
CheckDefFailure(['%s/a/b/'], 'E1050:')
@@ -844,11 +908,11 @@ def Test_block()
assert_equal(1, outer)
enddef
func Test_block_failure()
call CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:')
call CheckDefFailure(['}'], 'E1025:')
call CheckDefFailure(['{', 'echo 1'], 'E1026:')
endfunc
def Test_block_failure()
CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:')
CheckDefFailure(['}'], 'E1025:')
CheckDefFailure(['{', 'echo 1'], 'E1026:')
enddef
func g:NoSuchFunc()
echo 'none'
@@ -894,14 +958,14 @@ def Test_try_catch()
try
# string slice returns a string, not a number
n = g:astring[3]
catch /E1029:/
catch /E1012:/
n = 77
endtry
assert_equal(77, n)
try
n = l[g:astring]
catch /E1029:/
catch /E1012:/
n = 88
endtry
assert_equal(88, n)
@@ -952,7 +1016,7 @@ def Test_try_catch()
let nd: dict<any>
try
nd = {g:anumber: 1}
catch /E1029:/
catch /E1012:/
n = 266
endtry
assert_equal(266, n)
@@ -966,7 +1030,7 @@ def Test_try_catch()
try
&ts = g:astring
catch /E1029:/
catch /E1012:/
n = 288
endtry
assert_equal(288, n)
@@ -1105,6 +1169,26 @@ def Test_try_catch_nested()
assert_equal('finally', g:in_finally)
enddef
def TryOne(): number
try
return 0
catch
endtry
return 0
enddef
def TryTwo(n: number): string
try
let x = {}
catch
endtry
return 'text'
enddef
def Test_try_catch_twice()
assert_equal('text', TryOne()->TryTwo())
enddef
def Test_try_catch_match()
let seq = 'a'
try
@@ -1910,7 +1994,7 @@ def Test_import_compile_error()
source Ximport.vim
catch /E1001/
# Error should be fore the Xexported.vim file.
assert_match('E1001: variable not found: notDefined', v:exception)
assert_match('E1001: Variable not found: notDefined', v:exception)
assert_match('function <SNR>\d\+_ImpFunc\[1\]..<SNR>\d\+_ExpFunc, line 1', v:throwpoint)
endtry
@@ -3120,6 +3204,24 @@ def Test_let_type_check()
CheckScriptSuccess(lines)
enddef
let g:dict_number = #{one: 1, two: 2}
def Test_let_list_dict_type()
let ll: list<number>
ll = [1, 2, 2, 3, 3, 3]->uniq()
ll->assert_equal([1, 2, 3])
let dd: dict<number>
dd = g:dict_number
dd->assert_equal(g:dict_number)
let lines =<< trim END
let ll: list<number>
ll = [1, 2, 3]->map('"one"')
END
CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<string>')
enddef
def Test_forward_declaration()
let lines =<< trim END
vim9script
@@ -3308,6 +3410,14 @@ def Test_invalid_sid()
delete('Xdidit')
enddef
def Test_unset_any_variable()
let lines =<< trim END
let var: any
assert_equal(0, var)
END
CheckDefAndScriptSuccess(lines)
enddef
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new
+1 -1
View File
@@ -22,7 +22,7 @@
prepare_assert_error(garray_T *gap)
{
char buf[NUMBUFLEN];
char_u *sname = estack_sfile(FALSE);
char_u *sname = estack_sfile(ESTACK_NONE);
ga_init2(gap, 1, 100);
if (sname != NULL)
+2 -2
View File
@@ -512,8 +512,8 @@ tv_check_lock(typval_T *tv, char_u *name, int use_gettext)
default:
break;
}
return var_check_lock(tv->v_lock, name, use_gettext)
|| (lock != 0 && var_check_lock(lock, name, use_gettext));
return value_check_lock(tv->v_lock, name, use_gettext)
|| (lock != 0 && value_check_lock(lock, name, use_gettext));
}
/*
+13 -4
View File
@@ -874,6 +874,15 @@ find_func(char_u *name, int is_global, cctx_T *cctx)
return NULL;
}
/*
* Return TRUE if "ufunc" is a global function.
*/
int
func_is_global(ufunc_T *ufunc)
{
return ufunc->uf_name[0] != K_SPECIAL;
}
/*
* Copy the function name of "fp" to buffer "buf".
* "buf" must be able to hold the function name plus three bytes.
@@ -882,7 +891,7 @@ find_func(char_u *name, int is_global, cctx_T *cctx)
static void
cat_func_name(char_u *buf, ufunc_T *fp)
{
if (fp->uf_name[0] == K_SPECIAL)
if (!func_is_global(fp))
{
STRCPY(buf, "<SNR>");
STRCAT(buf, fp->uf_name + 3);
@@ -3141,7 +3150,7 @@ def_function(exarg_T *eap, char_u *name_arg)
}
// Check for ":append", ":change", ":insert". Not for :def.
p = skip_range(p, NULL);
p = skip_range(p, FALSE, NULL);
if (eap->cmdidx != CMD_def
&& ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|| (p[0] == 'c'
@@ -3337,11 +3346,11 @@ def_function(exarg_T *eap, char_u *name_arg)
if (fudi.fd_di == NULL)
{
// Can't add a function to a locked dictionary
if (var_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
goto erret;
}
// Can't change an existing function if it is locked
else if (var_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
goto erret;
// Give the function a sequential number. Can only be used with a
+100
View File
@@ -765,6 +765,106 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1701,
/**/
1700,
/**/
1699,
/**/
1698,
/**/
1697,
/**/
1696,
/**/
1695,
/**/
1694,
/**/
1693,
/**/
1692,
/**/
1691,
/**/
1690,
/**/
1689,
/**/
1688,
/**/
1687,
/**/
1686,
/**/
1685,
/**/
1684,
/**/
1683,
/**/
1682,
/**/
1681,
/**/
1680,
/**/
1679,
/**/
1678,
/**/
1677,
/**/
1676,
/**/
1675,
/**/
1674,
/**/
1673,
/**/
1672,
/**/
1671,
/**/
1670,
/**/
1669,
/**/
1668,
/**/
1667,
/**/
1666,
/**/
1665,
/**/
1664,
/**/
1663,
/**/
1662,
/**/
1661,
/**/
1660,
/**/
1659,
/**/
1658,
/**/
1657,
/**/
1656,
/**/
1655,
/**/
1654,
/**/
1653,
/**/
1652,
/**/
1651,
/**/
+10 -3
View File
@@ -2108,8 +2108,7 @@ typedef struct stat stat_T;
# define USE_PRINTF_FORMAT_ATTRIBUTE
#endif
typedef enum
{
typedef enum {
ASSERT_EQUAL,
ASSERT_NOTEQUAL,
ASSERT_MATCH,
@@ -2139,9 +2138,17 @@ typedef enum {
USEPOPUP_HIDDEN // use info popup initially hidden
} use_popup_T;
// Argument for estack_sfile().
typedef enum {
ESTACK_NONE,
ESTACK_SFILE,
ESTACK_STACK
} estack_arg_T;
// Flags for assignment functions.
#define LET_IS_CONST 1 // ":const"
#define LET_NO_COMMAND 2 // "var = expr" without ":let" or ":const"
#define LET_FORCEIT 2 // ":const!" (LET_IS_CONST is also set)
#define LET_NO_COMMAND 4 // "var = expr" without ":let" or ":const"
#include "ex_cmds.h" // Ex command defines
#include "spell.h" // spell checking stuff
+3 -1
View File
@@ -58,6 +58,8 @@ typedef enum {
ISN_UNLET, // unlet variable isn_arg.unlet.ul_name
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
ISN_LOCKCONST, // lock constant value
// constants
ISN_PUSHNR, // push number isn_arg.number
ISN_PUSHBOOL, // push bool value isn_arg.number
@@ -205,7 +207,7 @@ typedef struct {
// arguments to ISN_CHECKTYPE
typedef struct {
vartype_T ct_type;
type_T *ct_type;
int ct_off; // offset in stack, -1 is bottom
} checktype_T;
+127 -56
View File
@@ -292,12 +292,14 @@ lookup_script(char_u *name, size_t len, int vim9script)
/*
* Check if "p[len]" is already defined, either in script "import_sid" or in
* compilation context "cctx".
* Does not check the global namespace.
* Return FAIL and give an error if it defined.
*/
int
check_defined(char_u *p, size_t len, cctx_T *cctx)
{
int c = p[len];
int c = p[len];
ufunc_T *ufunc = NULL;
p[len] = NUL;
if (lookup_script(p, len, FALSE) == OK
@@ -305,11 +307,16 @@ check_defined(char_u *p, size_t len, cctx_T *cctx)
&& (lookup_local(p, len, cctx) != NULL
|| lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK))
|| find_imported(p, len, cctx) != NULL
|| find_func_even_dead(p, FALSE, cctx) != NULL)
|| (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL)
{
p[len] = c;
semsg(_(e_name_already_defined_str), p);
return FAIL;
// A local or script-local function can shadow a global function.
if (ufunc == NULL || !func_is_global(ufunc)
|| (p[0] == 'g' && p[1] == ':'))
{
p[len] = c;
semsg(_(e_name_already_defined_str), p);
return FAIL;
}
}
p[len] = c;
return OK;
@@ -697,7 +704,10 @@ generate_2BOOL(cctx_T *cctx, int invert)
}
static int
generate_TYPECHECK(cctx_T *cctx, type_T *vartype, int offset)
generate_TYPECHECK(
cctx_T *cctx,
type_T *expected,
int offset)
{
isn_T *isn;
garray_T *stack = &cctx->ctx_type_stack;
@@ -705,19 +715,18 @@ generate_TYPECHECK(cctx_T *cctx, type_T *vartype, int offset)
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL)
return FAIL;
// TODO: whole type, e.g. for a function also arg and return types
isn->isn_arg.type.ct_type = vartype->tt_type;
isn->isn_arg.type.ct_type = alloc_type(expected);
isn->isn_arg.type.ct_off = offset;
// type becomes vartype
((type_T **)stack->ga_data)[stack->ga_len + offset] = vartype;
// type becomes expected
((type_T **)stack->ga_data)[stack->ga_len + offset] = expected;
return OK;
}
/*
* Check that
* - "actual" is "expected" type or
* - "actual" matches "expected" type or
* - "actual" is a type that can be "expected" type: add a runtime check; or
* - return FAIL.
*/
@@ -740,17 +749,29 @@ need_type(
if (check_type(expected, actual, FALSE, 0) == OK)
return OK;
if (actual->tt_type != VAR_ANY
&& actual->tt_type != VAR_UNKNOWN
&& !(actual->tt_type == VAR_FUNC
&& (actual->tt_member == &t_any || actual->tt_argcount < 0)))
// If the actual type can be the expected type add a runtime check.
// TODO: if it's a constant a runtime check makes no sense.
if (actual->tt_type == VAR_ANY
|| actual->tt_type == VAR_UNKNOWN
|| (actual->tt_type == VAR_FUNC
&& (expected->tt_type == VAR_FUNC
|| expected->tt_type == VAR_PARTIAL)
&& (actual->tt_member == &t_any || actual->tt_argcount < 0))
|| (actual->tt_type == VAR_LIST
&& expected->tt_type == VAR_LIST
&& actual->tt_member == &t_any)
|| (actual->tt_type == VAR_DICT
&& expected->tt_type == VAR_DICT
&& actual->tt_member == &t_any))
{
if (!silent)
type_mismatch(expected, actual);
return FAIL;
generate_TYPECHECK(cctx, expected, offset);
return OK;
}
generate_TYPECHECK(cctx, expected, offset);
return OK;
if (!silent)
type_mismatch(expected, actual);
return FAIL;
}
/*
@@ -769,7 +790,7 @@ generate_PUSHNR(cctx_T *cctx, varnumber_T number)
if (number == 0 || number == 1)
{
type_T *type = alloc_type(cctx->ctx_type_list);
type_T *type = get_type_ptr(cctx->ctx_type_list);
// A 0 or 1 number can also be used as a bool.
if (type != NULL)
@@ -1101,6 +1122,20 @@ generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit)
return OK;
}
/*
* Generate an ISN_LOCKCONST instruction.
*/
static int
generate_LOCKCONST(cctx_T *cctx)
{
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_LOCKCONST)) == NULL)
return FAIL;
return OK;
}
/*
* Generate an ISN_LOADS instruction.
*/
@@ -1395,8 +1430,8 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
continue;
expected = ufunc->uf_arg_types[i];
}
else if (ufunc->uf_va_type == NULL)
// possibly a lambda
else if (ufunc->uf_va_type == NULL || ufunc->uf_va_type == &t_any)
// possibly a lambda or "...: any"
expected = &t_any;
else
expected = ufunc->uf_va_type->tt_member;
@@ -2114,10 +2149,16 @@ generate_funcref(cctx_T *cctx, char_u *name)
/*
* Compile a variable name into a load instruction.
* "end" points to just after the name.
* "is_expr" is TRUE when evaluating an expression, might be a funcref.
* When "error" is FALSE do not give an error when not found.
*/
static int
compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
compile_load(
char_u **arg,
char_u *end_arg,
cctx_T *cctx,
int is_expr,
int error)
{
type_T *type;
char_u *name = NULL;
@@ -2214,10 +2255,11 @@ compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
|| find_imported(name, 0, cctx) != NULL)
res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);
// When the name starts with an uppercase letter or "x:" it
// can be a user defined function.
// When evaluating an expression and the name starts with an
// uppercase letter or "x:" it can be a user defined function.
// TODO: this is just guessing
if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
if (res == FAIL && is_expr
&& (ASCII_ISUPPER(*name) || name[1] == ':'))
res = generate_funcref(cctx, name);
}
}
@@ -2368,8 +2410,9 @@ compile_call(
}
// If we can find the function by name generate the right call.
// Skip global functions here, a local funcref takes precedence.
ufunc = find_func(name, FALSE, cctx);
if (ufunc != NULL)
if (ufunc != NULL && !func_is_global(ufunc))
{
res = generate_CALL(cctx, ufunc, argcount);
goto theend;
@@ -2380,7 +2423,7 @@ compile_call(
// Not for eome#Func(), it will be loaded later.
p = namebuf;
if (STRNCMP(namebuf, "g:", 2) != 0 && !is_autoload
&& compile_load(&p, namebuf + varlen, cctx, FALSE) == OK)
&& compile_load(&p, namebuf + varlen, cctx, FALSE, FALSE) == OK)
{
garray_T *stack = &cctx->ctx_type_stack;
type_T *type;
@@ -2390,6 +2433,13 @@ compile_call(
goto theend;
}
// If we can find a global function by name generate the right call.
if (ufunc != NULL)
{
res = generate_CALL(cctx, ufunc, argcount);
goto theend;
}
// A global function may be defined only later. Need to figure out at
// runtime. Also handles a FuncRef at runtime.
if (STRNCMP(namebuf, "g:", 2) == 0 || is_autoload)
@@ -3548,7 +3598,7 @@ compile_expr7(
{
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
r = compile_load(arg, p, cctx, TRUE);
r = compile_load(arg, p, cctx, TRUE, TRUE);
}
if (r == FAIL)
return FAIL;
@@ -4001,7 +4051,7 @@ compile_and_or(
typep = ((type_T **)stack->ga_data) + stack->ga_len - 1;
if (*typep != &t_bool)
{
type_T *type = alloc_type(cctx->ctx_type_list);
type_T *type = get_type_ptr(cctx->ctx_type_list);
if (type != NULL)
{
@@ -4254,7 +4304,7 @@ compile_return(char_u *arg, int set_return_type, cctx_T *cctx)
}
if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1,
cctx, FALSE) == FAIL)
return NULL;
return NULL;
}
}
else
@@ -4320,6 +4370,12 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
ufunc_T *ufunc;
int r;
if (eap->forceit)
{
emsg(_(e_cannot_use_bang_with_nested_def));
return NULL;
}
// Only g:Func() can use a namespace.
if (name_start[1] == ':' && !is_global)
{
@@ -4772,11 +4828,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
semsg(_(e_variable_already_declared), name);
goto theend;
}
else if (lvar->lv_const)
{
semsg(_(e_cannot_assign_to_constant), name);
goto theend;
}
}
else
{
@@ -4932,6 +4983,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
semsg(_(e_cannot_assign_to_argument), name);
goto theend;
}
if (!is_decl && lvar != NULL && lvar->lv_const && !has_index)
{
semsg(_(e_cannot_assign_to_constant), name);
goto theend;
}
if (!heredoc)
{
@@ -4996,6 +5052,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
if (lvar != NULL && (is_decl || !has_type))
{
if ((stacktype->tt_type == VAR_FUNC
|| stacktype->tt_type == VAR_PARTIAL)
&& var_wrong_func_name(name, TRUE))
goto theend;
if (new_local && !has_type)
{
if (stacktype->tt_type == VAR_VOID)
@@ -5003,12 +5064,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
emsg(_(e_cannot_use_void_value));
goto theend;
}
else if ((stacktype->tt_type == VAR_FUNC
|| stacktype->tt_type == VAR_PARTIAL)
&& var_wrong_func_name(name, TRUE))
{
goto theend;
}
else
{
// An empty list or dict has a &t_void member,
@@ -5025,12 +5080,13 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
{
type_T *use_type = lvar->lv_type;
// without operator type is here, otherwise below
// without operator check type here, otherwise below
if (has_index)
{
use_type = use_type->tt_member;
if (use_type == NULL)
use_type = &t_void;
// could be indexing "any"
use_type = &t_any;
}
if (need_type(stacktype, use_type, -1, cctx, FALSE)
== FAIL)
@@ -5205,6 +5261,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
}
else
{
if (is_decl && eap->forceit && cmdidx == CMD_const
&& (dest == dest_script || dest == dest_local))
// ":const! var": lock the value, but not referenced variables
generate_LOCKCONST(cctx);
switch (dest)
{
case dest_option:
@@ -6335,13 +6396,8 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
char_u *line = arg;
linenr_T lnum;
char *errormsg;
int above = FALSE;
int above = eap->forceit;
if (*arg == '!')
{
above = TRUE;
line = skipwhite(arg + 1);
}
eap->regname = *line;
if (eap->regname == '=')
@@ -6384,7 +6440,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
if (eap->cmdidx >= 0 && eap->cmdidx < CMD_SIZE)
{
long argt = excmd_get_argt(eap->cmdidx);
long argt = eap->argt;
int usefilter = FALSE;
has_expr = argt & (EX_XFILE | EX_EXPAND);
@@ -6803,7 +6859,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
cmd = ea.cmd;
if (*cmd != '\'' || starts_with_colon)
{
ea.cmd = skip_range(ea.cmd, NULL);
ea.cmd = skip_range(ea.cmd, TRUE, NULL);
if (ea.cmd > cmd)
{
if (!starts_with_colon)
@@ -6843,8 +6899,6 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
}
}
p = skipwhite(p);
if (cctx.ctx_had_return
&& ea.cmdidx != CMD_elseif
&& ea.cmdidx != CMD_else
@@ -6859,6 +6913,19 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
goto erret;
}
p = skipwhite(p);
if (ea.cmdidx != CMD_SIZE
&& ea.cmdidx != CMD_write && ea.cmdidx != CMD_read)
{
if (ea.cmdidx >= 0)
ea.argt = excmd_get_argt(ea.cmdidx);
if ((ea.argt & EX_BANG) && *p == '!')
{
ea.forceit = TRUE;
p = skipwhite(p + 1);
}
}
switch (ea.cmdidx)
{
case CMD_def:
@@ -7237,6 +7304,10 @@ delete_instr(isn_T *isn)
}
break;
case ISN_CHECKTYPE:
free_type(isn->isn_arg.type.ct_type);
break;
case ISN_2BOOL:
case ISN_2STRING:
case ISN_2STRING_ANY:
@@ -7248,7 +7319,6 @@ delete_instr(isn_T *isn)
case ISN_CATCH:
case ISN_CHECKLEN:
case ISN_CHECKNR:
case ISN_CHECKTYPE:
case ISN_COMPAREANY:
case ISN_COMPAREBLOB:
case ISN_COMPAREBOOL:
@@ -7282,6 +7352,7 @@ delete_instr(isn_T *isn)
case ISN_LOADTDICT:
case ISN_LOADV:
case ISN_LOADWDICT:
case ISN_LOCKCONST:
case ISN_MEMBER:
case ISN_NEGATENR:
case ISN_NEWDICT:
+62 -17
View File
@@ -677,6 +677,21 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
return OK;
}
/*
* Check if "lock" is VAR_LOCKED or VAR_FIXED. If so give an error and return
* TRUE.
*/
static int
error_if_locked(int lock, char *error)
{
if (lock & (VAR_LOCKED | VAR_FIXED))
{
emsg(_(error));
return TRUE;
}
return FALSE;
}
/*
* Store "tv" in variable "name".
* This is for s: and g: variables.
@@ -814,6 +829,7 @@ call_def_function(
// Check the type of the list items.
tv = STACK_TV_BOT(-1);
if (ufunc->uf_va_type != NULL
&& ufunc->uf_va_type != &t_any
&& ufunc->uf_va_type->tt_member != &t_any
&& tv->vval.v_list != NULL)
{
@@ -1455,12 +1471,12 @@ call_def_function(
typval_T *tv_list = STACK_TV_BOT(-1);
list_T *list = tv_list->vval.v_list;
SOURCING_LNUM = iptr->isn_lnum;
if (lidx < 0 && list->lv_len + lidx >= 0)
// negative index is relative to the end
lidx = list->lv_len + lidx;
if (lidx < 0 || lidx > list->lv_len)
{
SOURCING_LNUM = iptr->isn_lnum;
semsg(_(e_listidx), lidx);
goto on_error;
}
@@ -1469,12 +1485,18 @@ call_def_function(
{
listitem_T *li = list_find(list, lidx);
if (error_if_locked(li->li_tv.v_lock,
e_cannot_change_list_item))
goto failed;
// overwrite existing list item
clear_tv(&li->li_tv);
li->li_tv = *tv;
}
else
{
if (error_if_locked(list->lv_lock,
e_cannot_change_list))
goto failed;
// append to list, only fails when out of memory
if (list_append_tv(list, tv) == FAIL)
goto failed;
@@ -1495,9 +1517,9 @@ call_def_function(
dict_T *dict = tv_dict->vval.v_dict;
dictitem_T *di;
SOURCING_LNUM = iptr->isn_lnum;
if (dict == NULL)
{
SOURCING_LNUM = iptr->isn_lnum;
emsg(_(e_dictionary_not_set));
goto on_error;
}
@@ -1507,12 +1529,18 @@ call_def_function(
di = dict_find(dict, key, -1);
if (di != NULL)
{
if (error_if_locked(di->di_tv.v_lock,
e_cannot_change_dict_item))
goto failed;
// overwrite existing value
clear_tv(&di->di_tv);
di->di_tv = *tv;
}
else
{
if (error_if_locked(dict->dv_lock,
e_cannot_change_dict))
goto failed;
// add to dict, only fails when out of memory
if (dict_add_tv(dict, (char *)key, tv) == FAIL)
goto failed;
@@ -1603,6 +1631,10 @@ call_def_function(
vim_unsetenv(iptr->isn_arg.unlet.ul_name);
break;
case ISN_LOCKCONST:
item_lock(STACK_TV_BOT(-1), 100, TRUE, TRUE);
break;
// create a list from items on the stack; uses a single allocation
// for the list header and the items
case ISN_NEWLIST:
@@ -1665,6 +1697,7 @@ call_def_function(
// call a :def function
case ISN_DCALL:
SOURCING_LNUM = iptr->isn_lnum;
if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
iptr->isn_arg.dfunc.cdf_argcount,
&ectx) == FAIL)
@@ -1889,6 +1922,7 @@ call_def_function(
trycmd->tcd_catch_idx = iptr->isn_arg.try.try_catch;
trycmd->tcd_finally_idx = iptr->isn_arg.try.try_finally;
trycmd->tcd_caught = FALSE;
trycmd->tcd_return = FALSE;
}
break;
@@ -2125,6 +2159,7 @@ call_def_function(
exptype_T exptype = iptr->isn_arg.op.op_type;
int ic = iptr->isn_arg.op.op_ic;
SOURCING_LNUM = iptr->isn_lnum;
typval_compare(tv1, tv2, exptype, ic);
clear_tv(tv2);
--ectx.ec_stack.ga_len;
@@ -2502,18 +2537,19 @@ call_def_function(
checktype_T *ct = &iptr->isn_arg.type;
tv = STACK_TV_BOT(ct->ct_off);
// TODO: better type comparison
if (tv->v_type != ct->ct_type
&& !((tv->v_type == VAR_PARTIAL
&& ct->ct_type == VAR_FUNC)
|| (tv->v_type == VAR_FUNC
&& ct->ct_type == VAR_PARTIAL)))
{
SOURCING_LNUM = iptr->isn_lnum;
semsg(_(e_expected_str_but_got_str),
vartype_name(ct->ct_type),
vartype_name(tv->v_type));
SOURCING_LNUM = iptr->isn_lnum;
if (check_typval_type(ct->ct_type, tv, 0) == FAIL)
goto on_error;
// number 0 is FALSE, number 1 is TRUE
if (tv->v_type == VAR_NUMBER
&& ct->ct_type->tt_type == VAR_BOOL
&& (tv->vval.v_number == 0
|| tv->vval.v_number == 1))
{
tv->v_type = VAR_BOOL;
tv->vval.v_number = tv->vval.v_number
? VVAL_TRUE : VVAL_FALSE;
}
}
break;
@@ -3011,6 +3047,9 @@ ex_disassemble(exarg_T *eap)
iptr->isn_arg.unlet.ul_forceit ? "!" : "",
iptr->isn_arg.unlet.ul_name);
break;
case ISN_LOCKCONST:
smsg("%4d LOCKCONST", current);
break;
case ISN_NEWLIST:
smsg("%4d NEWLIST size %lld", current,
(long long)(iptr->isn_arg.number));
@@ -3238,10 +3277,16 @@ ex_disassemble(exarg_T *eap)
case ISN_NEGATENR: smsg("%4d NEGATENR", current); break;
case ISN_CHECKNR: smsg("%4d CHECKNR", current); break;
case ISN_CHECKTYPE: smsg("%4d CHECKTYPE %s stack[%d]", current,
vartype_name(iptr->isn_arg.type.ct_type),
iptr->isn_arg.type.ct_off);
break;
case ISN_CHECKTYPE:
{
char *tofree;
smsg("%4d CHECKTYPE %s stack[%d]", current,
type_name(iptr->isn_arg.type.ct_type, &tofree),
iptr->isn_arg.type.ct_off);
vim_free(tofree);
break;
}
case ISN_CHECKLEN: smsg("%4d CHECKLEN %s%d", current,
iptr->isn_arg.checklen.cl_more_OK ? ">= " : "",
iptr->isn_arg.checklen.cl_min_len);
+5 -1
View File
@@ -548,7 +548,11 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
// Create the variable with 0/NULL value.
CLEAR_FIELD(init_tv);
init_tv.v_type = type->tt_type;
if (type->tt_type == VAR_ANY)
// A variable of type "any" is not possible, just use zero instead
init_tv.v_type = VAR_NUMBER;
else
init_tv.v_type = type->tt_type;
set_var_const(name, type, &init_tv, FALSE, 0);
vim_free(name);
+87 -22
View File
@@ -22,10 +22,10 @@
/*
* Allocate memory for a type_T and add the pointer to type_gap, so that it can
* be freed later.
* be easily freed later.
*/
type_T *
alloc_type(garray_T *type_gap)
get_type_ptr(garray_T *type_gap)
{
type_T *type;
@@ -48,6 +48,60 @@ clear_type_list(garray_T *gap)
ga_clear(gap);
}
/*
* Take a type that is using entries in a growarray and turn it into a type
* with allocated entries.
*/
type_T *
alloc_type(type_T *type)
{
type_T *ret;
if (type == NULL)
return NULL;
// A fixed type never contains allocated types, return as-is.
if (type->tt_flags & TTFLAG_STATIC)
return type;
ret = ALLOC_ONE(type_T);
*ret = *type;
if (ret->tt_member != NULL)
ret->tt_member = alloc_type(ret->tt_member);
if (type->tt_args != NULL)
{
int i;
ret->tt_args = ALLOC_MULT(type_T *, type->tt_argcount);
if (ret->tt_args != NULL)
for (i = 0; i < type->tt_argcount; ++i)
ret->tt_args[i] = alloc_type(type->tt_args[i]);
}
return ret;
}
/*
* Free a type that was created with alloc_type().
*/
void
free_type(type_T *type)
{
int i;
if (type == NULL || (type->tt_flags & TTFLAG_STATIC))
return;
if (type->tt_args != NULL)
{
for (i = 0; i < type->tt_argcount; ++i)
free_type(type->tt_args[i]);
vim_free(type->tt_args);
}
free_type(type->tt_member);
vim_free(type);
}
type_T *
get_list_type(type_T *member_type, garray_T *type_gap)
{
@@ -67,7 +121,7 @@ get_list_type(type_T *member_type, garray_T *type_gap)
return &t_list_string;
// Not a common type, create a new entry.
type = alloc_type(type_gap);
type = get_type_ptr(type_gap);
if (type == NULL)
return &t_any;
type->tt_type = VAR_LIST;
@@ -96,7 +150,7 @@ get_dict_type(type_T *member_type, garray_T *type_gap)
return &t_dict_string;
// Not a common type, create a new entry.
type = alloc_type(type_gap);
type = get_type_ptr(type_gap);
if (type == NULL)
return &t_any;
type->tt_type = VAR_DICT;
@@ -112,7 +166,7 @@ get_dict_type(type_T *member_type, garray_T *type_gap)
type_T *
alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap)
{
type_T *type = alloc_type(type_gap);
type_T *type = get_type_ptr(type_gap);
if (type == NULL)
return &t_any;
@@ -197,13 +251,14 @@ func_type_add_arg_types(
/*
* Get a type_T for a typval_T.
* "type_list" is used to temporarily create types in.
* "type_gap" is used to temporarily create types in.
*/
static type_T *
typval2type_int(typval_T *tv, garray_T *type_gap)
{
type_T *type;
type_T *member_type;
type_T *member_type = &t_any;
int argcount = 0;
if (tv->v_type == VAR_NUMBER)
return &t_number;
@@ -262,8 +317,18 @@ typval2type_int(typval_T *tv, garray_T *type_gap)
else
name = tv->vval.v_string;
if (name != NULL)
// TODO: how about a builtin function?
ufunc = find_func(name, FALSE, NULL);
{
int idx = find_internal_func(name);
if (idx >= 0)
{
// TODO: get actual arg count and types
argcount = -1;
member_type = internal_func_ret_type(idx, 0, NULL);
}
else
ufunc = find_func(name, FALSE, NULL);
}
if (ufunc != NULL)
{
// May need to get the argument types from default values by
@@ -276,11 +341,12 @@ typval2type_int(typval_T *tv, garray_T *type_gap)
}
}
type = alloc_type(type_gap);
type = get_type_ptr(type_gap);
if (type == NULL)
return NULL;
type->tt_type = tv->v_type;
type->tt_member = &t_any;
type->tt_argcount = argcount;
type->tt_member = member_type;
return type;
}
@@ -311,7 +377,7 @@ typval2type(typval_T *tv, garray_T *type_gap)
&& (tv->vval.v_number == 0 || tv->vval.v_number == 1))
|| (tv->v_lock & VAR_BOOL_OK)))
{
type_T *newtype = alloc_type(type_gap);
type_T *newtype = get_type_ptr(type_gap);
// Number 0 and 1 and expression with "&&" or "||" can also be used
// for bool.
@@ -420,6 +486,7 @@ check_type(type_T *expected, type_T *actual, int give_msg, int argidx)
ret = check_type(expected->tt_member, actual->tt_member,
FALSE, 0);
if (ret == OK && expected->tt_argcount != -1
&& actual->tt_argcount != -1
&& (actual->tt_argcount < expected->tt_min_argcount
|| actual->tt_argcount > expected->tt_argcount))
ret = FAIL;
@@ -940,7 +1007,6 @@ type_name(type_T *type, char **tofree)
ga_init2(&ga, 1, 100);
if (ga_grow(&ga, 20) == FAIL)
return "[unknown]";
*tofree = ga.ga_data;
STRCPY(ga.ga_data, "func(");
ga.ga_len += 5;
@@ -963,20 +1029,19 @@ type_name(type_T *type, char **tofree)
if (ga_grow(&ga, len + 8) == FAIL)
{
vim_free(arg_free);
ga_clear(&ga);
return "[unknown]";
}
*tofree = ga.ga_data;
if (varargs && i == type->tt_argcount - 1)
{
STRCPY((char *)ga.ga_data + ga.ga_len, "...");
ga.ga_len += 3;
}
ga_concat(&ga, (char_u *)"...");
else if (i >= type->tt_min_argcount)
*((char *)ga.ga_data + ga.ga_len++) = '?';
STRCPY((char *)ga.ga_data + ga.ga_len, arg_type);
ga.ga_len += len;
ga_concat(&ga, (char_u *)arg_type);
vim_free(arg_free);
}
if (type->tt_argcount < 0)
// any number of arguments
ga_concat(&ga, (char_u *)"...");
if (type->tt_member == &t_void)
STRCPY((char *)ga.ga_data + ga.ga_len, ")");
@@ -990,18 +1055,18 @@ type_name(type_T *type, char **tofree)
if (ga_grow(&ga, len) == FAIL)
{
vim_free(ret_free);
ga_clear(&ga);
return "[unknown]";
}
*tofree = ga.ga_data;
STRCPY((char *)ga.ga_data + ga.ga_len, "): ");
STRCPY((char *)ga.ga_data + ga.ga_len + 3, ret_name);
vim_free(ret_free);
}
*tofree = ga.ga_data;
return ga.ga_data;
}
return name;
}
#endif // FEAT_EVAL