patch 9.1.0020: Vim9: cannot compile all methods in a class

Commit: 
https://github.com/vim/vim/commit/4f32c83a775a195ae7e1545b2840fb773f93414f
Author: Yegappan Lakshmanan <[email protected]>
Date:   Fri Jan 12 17:36:40 2024 +0100

    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 <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/tags b/runtime/doc/tags
index bab6b3bf5..b3691bb93 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -6428,6 +6428,7 @@ cino-{    indent.txt      /*cino-{*
 cino-} indent.txt      /*cino-}*
 cinoptions-values      indent.txt      /*cinoptions-values*
 class  vim9class.txt   /*class*
+class-compile  vim9class.txt   /*class-compile*
 class-method   vim9class.txt   /*class-method*
 clear-undo     undo.txt        /*clear-undo*
 clearmatches() builtin.txt     /*clearmatches()*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index 9847ff7d3..746488ecc 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -130,8 +130,6 @@ Further Vim9 improvements:
     Issue #11822: any.Func() can be a dict or an object call, need to handle
     this at runtime.  Also see #12198 for an example.
     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.
        Email lifepillar 2023 Mar 26
   - object empty(), len() - can class define a method to be used for them?
diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt
index b12b1cc99..b246fcbce 100644
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -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
@@ -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.
 
                                                *:defc* *:defcompile*
-:defc[ompile]          Compile functions defined in the current script that
-                       were not compiled yet.
-                       This will report any errors found during compilation.
-                       This excludes functions defined inside a class.
+:defc[ompile]          Compile functions and classes (|class-compile|)
+                       defined in the current script that were not compiled
+                       yet.  This will report any errors found during
+                       compilation.
+
+:defc[ompile] MyClass  Compile all methods in a class |class-compile|.
 
 :defc[ompile] {func}
 :defc[ompile] debug {func}
diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt
index 6e94e8483..ba821c1b2 100644
--- a/runtime/doc/vim9class.txt
+++ b/runtime/doc/vim9class.txt
@@ -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
@@ -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
 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*
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index e393c0470..9bb461663 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -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);
 void ex_function(exarg_T *eap);
 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);
 int eval_fname_script(char_u *p);
 int translated_function_exists(char_u *name, int is_global);
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
index f1b63602a..a746eb772 100644
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -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 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 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);
 void f_instanceof(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index b34d2ad12..62a6d043d 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -9686,4 +9686,87 @@ def Test_method_double_underscore_prefix()
   v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3)
 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
diff --git a/src/userfunc.c b/src/userfunc.c
index 64761ecdb..e39ce6e49 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -5545,6 +5545,60 @@ find_func_by_name(char_u *name, compiletype_T 
*compile_type)
     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
  * be compiled or the one specified by the argument.
@@ -5555,46 +5609,29 @@ ex_defcompile(exarg_T *eap)
 {
     if (*eap->arg != NUL)
     {
-       compiletype_T compile_type = CT_NONE;
-       ufunc_T *ufunc = find_func_by_name(eap->arg, &compile_type);
-       if (ufunc != NULL)
+       typval_T tv;
+
+       if (is_class_name(eap->arg, &tv))
        {
-           if (func_needs_compiling(ufunc, compile_type))
-               (void)compile_def_function(ufunc, FALSE, compile_type, NULL);
-           else
-               smsg(_("Function %s does not need compiling"), eap->arg);
+           class_T *cl = tv.vval.v_class;
+
+           if (cl != NULL)
+               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
     {
-       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);
+       defcompile_funcs_in_script();
 
-                   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;
-                   }
-               }
-           }
-       }
+       // compile all the class defined in the current script
+       defcompile_classes_in_script();
     }
 }
 
diff --git a/src/version.c b/src/version.c
index 627acd24d..76a7668b0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    20,
 /**/
     19,
 /**/
diff --git a/src/vim9class.c b/src/vim9class.c
index e5d9aeb4b..525f8d038 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -3224,6 +3224,54 @@ member_not_found_msg(class_T *cl, vartype_T v_type, 
char_u *name, size_t len)
     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
  * interfaces matches the class "other_cl".

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1rOKea-008giR-Bq%40256bit.org.

Raspunde prin e-mail lui