mirror of
https://github.com/vim/vim.git
synced 2025-08-26 20:03:41 -04:00
patch 9.1.0020: Vim9: cannot compile all methods in a class
Problem: Vim9: cannot compile all methods in a class Solution: Support compiling all the methods in a class using :defcompile (Yegappan Lakshmanan) closes: #13844 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
8610f74382
commit
4f32c83a77
@ -6428,6 +6428,7 @@ cino-{ indent.txt /*cino-{*
|
|||||||
cino-} indent.txt /*cino-}*
|
cino-} indent.txt /*cino-}*
|
||||||
cinoptions-values indent.txt /*cinoptions-values*
|
cinoptions-values indent.txt /*cinoptions-values*
|
||||||
class vim9class.txt /*class*
|
class vim9class.txt /*class*
|
||||||
|
class-compile vim9class.txt /*class-compile*
|
||||||
class-method vim9class.txt /*class-method*
|
class-method vim9class.txt /*class-method*
|
||||||
clear-undo undo.txt /*clear-undo*
|
clear-undo undo.txt /*clear-undo*
|
||||||
clearmatches() builtin.txt /*clearmatches()*
|
clearmatches() builtin.txt /*clearmatches()*
|
||||||
|
@ -130,8 +130,6 @@ Further Vim9 improvements:
|
|||||||
Issue #11822: any.Func() can be a dict or an object call, need to handle
|
Issue #11822: any.Func() can be a dict or an object call, need to handle
|
||||||
this at runtime. Also see #12198 for an example.
|
this at runtime. Also see #12198 for an example.
|
||||||
Possibly issue #11981 can be fixed at the same time (has two examples).
|
Possibly issue #11981 can be fixed at the same time (has two examples).
|
||||||
- Make ":defcompile ClassName" compile all functions and methods in the
|
|
||||||
class.
|
|
||||||
- Forward declaration of a class? E.g. for Clone() function.
|
- Forward declaration of a class? E.g. for Clone() function.
|
||||||
Email lifepillar 2023 Mar 26
|
Email lifepillar 2023 Mar 26
|
||||||
- object empty(), len() - can class define a method to be used for them?
|
- object empty(), len() - can class define a method to be used for them?
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
*vim9.txt* For Vim version 9.1. Last change: 2023 Dec 24
|
*vim9.txt* For Vim version 9.1. Last change: 2024 Jan 12
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -1260,10 +1260,12 @@ Script-local variables in a |Vim9| script must be declared at the script
|
|||||||
level. They cannot be created in a function, also not in a legacy function.
|
level. They cannot be created in a function, also not in a legacy function.
|
||||||
|
|
||||||
*:defc* *:defcompile*
|
*:defc* *:defcompile*
|
||||||
:defc[ompile] Compile functions defined in the current script that
|
:defc[ompile] Compile functions and classes (|class-compile|)
|
||||||
were not compiled yet.
|
defined in the current script that were not compiled
|
||||||
This will report any errors found during compilation.
|
yet. This will report any errors found during
|
||||||
This excludes functions defined inside a class.
|
compilation.
|
||||||
|
|
||||||
|
:defc[ompile] MyClass Compile all methods in a class |class-compile|.
|
||||||
|
|
||||||
:defc[ompile] {func}
|
:defc[ompile] {func}
|
||||||
:defc[ompile] debug {func}
|
:defc[ompile] debug {func}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
*vim9class.txt* For Vim version 9.1. Last change: 2024 Jan 06
|
*vim9class.txt* For Vim version 9.1. Last change: 2024 Jan 12
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -830,7 +830,14 @@ Note that the method name must start with "new". If there is no method called
|
|||||||
"new()" then the default constructor is added, even though there are other
|
"new()" then the default constructor is added, even though there are other
|
||||||
constructor methods.
|
constructor methods.
|
||||||
|
|
||||||
|
Compiling methods in a Class ~
|
||||||
|
*class-compile*
|
||||||
|
The |:defcompile| command can be used to compile all the class and object
|
||||||
|
methods defined in a class: >
|
||||||
|
|
||||||
|
defcompile MyClass # Compile class "MyClass"
|
||||||
|
defcompile # Compile the classes in the current script
|
||||||
|
<
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
7. Type definition *typealias* *Vim9-type* *:type*
|
7. Type definition *typealias* *Vim9-type* *:type*
|
||||||
|
@ -50,6 +50,7 @@ void list_functions(regmatch_T *regmatch);
|
|||||||
ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int class_flags, ocmember_T *obj_members, int obj_member_count);
|
ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int class_flags, ocmember_T *obj_members, int obj_member_count);
|
||||||
void ex_function(exarg_T *eap);
|
void ex_function(exarg_T *eap);
|
||||||
ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
|
ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
|
||||||
|
void defcompile_function(ufunc_T *ufunc, class_T *cl);
|
||||||
void ex_defcompile(exarg_T *eap);
|
void ex_defcompile(exarg_T *eap);
|
||||||
int eval_fname_script(char_u *p);
|
int eval_fname_script(char_u *p);
|
||||||
int translated_function_exists(char_u *name, int is_global);
|
int translated_function_exists(char_u *name, int is_global);
|
||||||
|
@ -31,6 +31,9 @@ void object_free_items(int copyID);
|
|||||||
void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl);
|
void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl);
|
||||||
void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
|
void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
|
||||||
void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
|
void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
|
||||||
|
void defcompile_class(class_T *cl);
|
||||||
|
void defcompile_classes_in_script(void);
|
||||||
|
int is_class_name(char_u *name, typval_T *rettv);
|
||||||
int class_instance_of(class_T *cl, class_T *other_cl);
|
int class_instance_of(class_T *cl, class_T *other_cl);
|
||||||
void f_instanceof(typval_T *argvars, typval_T *rettv);
|
void f_instanceof(typval_T *argvars, typval_T *rettv);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@ -9686,4 +9686,87 @@ def Test_method_double_underscore_prefix()
|
|||||||
v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3)
|
v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Test for compiling class/object methods using :defcompile
|
||||||
|
def Test_defcompile_class()
|
||||||
|
# defcompile all the classes in the current script
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def Foo()
|
||||||
|
var i = 10
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
class B
|
||||||
|
def Bar()
|
||||||
|
var i = 20
|
||||||
|
xxx
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
defcompile
|
||||||
|
END
|
||||||
|
v9.CheckSourceFailure(lines, 'E476: Invalid command: xxx', 2)
|
||||||
|
|
||||||
|
# defcompile a specific class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def Foo()
|
||||||
|
xxx
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
class B
|
||||||
|
def Bar()
|
||||||
|
yyy
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
defcompile B
|
||||||
|
END
|
||||||
|
v9.CheckSourceFailure(lines, 'E476: Invalid command: yyy', 1)
|
||||||
|
|
||||||
|
# defcompile a non-class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def Foo()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var X: list<number> = []
|
||||||
|
defcompile X
|
||||||
|
END
|
||||||
|
v9.CheckSourceFailure(lines, 'E1061: Cannot find function X', 7)
|
||||||
|
|
||||||
|
# defcompile a class twice
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def new()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
defcompile A
|
||||||
|
defcompile A
|
||||||
|
assert_equal('Function A.new does not need compiling', v:statusmsg)
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
# defcompile should not compile an imported class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
export class A
|
||||||
|
def Foo()
|
||||||
|
xxx
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
writefile(lines, 'Xdefcompileimport.vim', 'D')
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
import './Xdefcompileimport.vim'
|
||||||
|
class B
|
||||||
|
endclass
|
||||||
|
defcompile
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
105
src/userfunc.c
105
src/userfunc.c
@ -5545,6 +5545,60 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
|
|||||||
return ufunc;
|
return ufunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile the :def function "ufunc". If "cl" is not NULL, then compile the
|
||||||
|
* class or object method "ufunc" in "cl".
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
defcompile_function(ufunc_T *ufunc, class_T *cl)
|
||||||
|
{
|
||||||
|
compiletype_T compile_type = CT_NONE;
|
||||||
|
|
||||||
|
if (func_needs_compiling(ufunc, compile_type))
|
||||||
|
(void)compile_def_function(ufunc, FALSE, compile_type, NULL);
|
||||||
|
else
|
||||||
|
smsg(_("Function %s%s%s does not need compiling"),
|
||||||
|
cl != NULL ? cl->class_name : (char_u *)"",
|
||||||
|
cl != NULL ? (char_u *)"." : (char_u *)"",
|
||||||
|
ufunc->uf_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile all the :def functions defined in the current script
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
defcompile_funcs_in_script(void)
|
||||||
|
{
|
||||||
|
long todo = (long)func_hashtab.ht_used;
|
||||||
|
int changed = func_hashtab.ht_changed;
|
||||||
|
hashitem_T *hi;
|
||||||
|
|
||||||
|
for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi)
|
||||||
|
{
|
||||||
|
if (!HASHITEM_EMPTY(hi))
|
||||||
|
{
|
||||||
|
--todo;
|
||||||
|
ufunc_T *ufunc = HI2UF(hi);
|
||||||
|
if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
|
||||||
|
&& ufunc->uf_def_status == UF_TO_BE_COMPILED
|
||||||
|
&& (ufunc->uf_flags & FC_DEAD) == 0)
|
||||||
|
{
|
||||||
|
(void)compile_def_function(ufunc, FALSE, CT_NONE, NULL);
|
||||||
|
|
||||||
|
if (func_hashtab.ht_changed != changed)
|
||||||
|
{
|
||||||
|
// a function has been added or removed, need to start
|
||||||
|
// over
|
||||||
|
todo = (long)func_hashtab.ht_used;
|
||||||
|
changed = func_hashtab.ht_changed;
|
||||||
|
hi = func_hashtab.ht_array;
|
||||||
|
--hi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* :defcompile - compile all :def functions in the current script that need to
|
* :defcompile - compile all :def functions in the current script that need to
|
||||||
* be compiled or the one specified by the argument.
|
* be compiled or the one specified by the argument.
|
||||||
@ -5555,46 +5609,29 @@ ex_defcompile(exarg_T *eap)
|
|||||||
{
|
{
|
||||||
if (*eap->arg != NUL)
|
if (*eap->arg != NUL)
|
||||||
{
|
{
|
||||||
compiletype_T compile_type = CT_NONE;
|
typval_T tv;
|
||||||
ufunc_T *ufunc = find_func_by_name(eap->arg, &compile_type);
|
|
||||||
if (ufunc != NULL)
|
if (is_class_name(eap->arg, &tv))
|
||||||
{
|
{
|
||||||
if (func_needs_compiling(ufunc, compile_type))
|
class_T *cl = tv.vval.v_class;
|
||||||
(void)compile_def_function(ufunc, FALSE, compile_type, NULL);
|
|
||||||
else
|
if (cl != NULL)
|
||||||
smsg(_("Function %s does not need compiling"), eap->arg);
|
defcompile_class(cl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
compiletype_T compile_type = CT_NONE;
|
||||||
|
ufunc_T *ufunc = find_func_by_name(eap->arg, &compile_type);
|
||||||
|
if (ufunc != NULL)
|
||||||
|
defcompile_function(ufunc, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
long todo = (long)func_hashtab.ht_used;
|
defcompile_funcs_in_script();
|
||||||
int changed = func_hashtab.ht_changed;
|
|
||||||
hashitem_T *hi;
|
|
||||||
|
|
||||||
for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi)
|
// compile all the class defined in the current script
|
||||||
{
|
defcompile_classes_in_script();
|
||||||
if (!HASHITEM_EMPTY(hi))
|
|
||||||
{
|
|
||||||
--todo;
|
|
||||||
ufunc_T *ufunc = HI2UF(hi);
|
|
||||||
if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
|
|
||||||
&& ufunc->uf_def_status == UF_TO_BE_COMPILED
|
|
||||||
&& (ufunc->uf_flags & FC_DEAD) == 0)
|
|
||||||
{
|
|
||||||
(void)compile_def_function(ufunc, FALSE, CT_NONE, NULL);
|
|
||||||
|
|
||||||
if (func_hashtab.ht_changed != changed)
|
|
||||||
{
|
|
||||||
// a function has been added or removed, need to start
|
|
||||||
// over
|
|
||||||
todo = (long)func_hashtab.ht_used;
|
|
||||||
changed = func_hashtab.ht_changed;
|
|
||||||
hi = func_hashtab.ht_array;
|
|
||||||
--hi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
20,
|
||||||
/**/
|
/**/
|
||||||
19,
|
19,
|
||||||
/**/
|
/**/
|
||||||
|
@ -3224,6 +3224,54 @@ member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
|
|||||||
vim_free(varname);
|
vim_free(varname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile all the class and object methods in "cl".
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
defcompile_class(class_T *cl)
|
||||||
|
{
|
||||||
|
for (int loop = 1; loop <= 2; ++loop)
|
||||||
|
{
|
||||||
|
int func_count = loop == 1 ? cl->class_class_function_count
|
||||||
|
: cl->class_obj_method_count;
|
||||||
|
for (int i = 0; i < func_count; i++)
|
||||||
|
{
|
||||||
|
ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
|
||||||
|
: cl->class_obj_methods[i];
|
||||||
|
defcompile_function(ufunc, cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile all the classes defined in the current script
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
defcompile_classes_in_script(void)
|
||||||
|
{
|
||||||
|
for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
|
||||||
|
{
|
||||||
|
if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
|
||||||
|
EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
|
||||||
|
defcompile_class(cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns TRUE if "name" is the name of a class. The typval for the class is
|
||||||
|
* returned in "rettv".
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
is_class_name(char_u *name, typval_T *rettv)
|
||||||
|
{
|
||||||
|
rettv->v_type = VAR_UNKNOWN;
|
||||||
|
|
||||||
|
if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
|
||||||
|
EVAL_VAR_NO_FUNC) != FAIL)
|
||||||
|
return rettv->v_type == VAR_CLASS;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return TRUE when the class "cl", its base class or one of the implemented
|
* Return TRUE when the class "cl", its base class or one of the implemented
|
||||||
* interfaces matches the class "other_cl".
|
* interfaces matches the class "other_cl".
|
||||||
|
Loading…
x
Reference in New Issue
Block a user