mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-11 15:37:29 +02:00
Merge remote-tracking branch 'vim/master'
This commit is contained in:
+37
-2
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 : */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 don’t have an intrinsic meaning. The
|
||||
* score range isn’t 0 to 100. It’s 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 DON’T match the first three letters. Which
|
||||
* effectively rewards matching near the start. However there’s 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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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|
|
||||
@@ -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|
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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&
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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()
|
||||
|
||||
+315
-235
File diff suppressed because it is too large
Load Diff
@@ -180,9 +180,9 @@ def Test_assignment()
|
||||
CheckDefFailure(['¬ex += 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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
/**/
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user