patch 9.0.2040: trim(): hard to use default mask

Problem:  trim(): hard to use default mask
Solution: Use default 'mask' when it is v:none

The default 'mask' value is pretty complex, as it includes many
characters.  Yet, if one needs to specify the trimming direction, the
third argument, 'trim()' currently requires the 'mask' value to be
provided explicitly.

'v:none' is already used to mean "use the default argument value" in
user defined functions.  See |none-function_argument| in help.

closes: #13363

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Illia Bobyr <illia.bobyr@gmail.com>
diff --git a/src/errors.h b/src/errors.h
index 7b1181a..d7ed81a 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3539,6 +3539,8 @@
 EXTERN char e_cannot_lock_class_variable_str[]
 	INIT(= N_("E1392: Cannot (un)lock class variable \"%s\" in class \"%s\""));
 #endif
+EXTERN char e_string_or_none_required_for_argument_nr[]
+	INIT(= N_("E1393: String or v:none required for argument %d"));
 // E1393 - E1499 unused (reserved for Vim9 class support)
 EXTERN char e_cannot_mix_positional_and_non_positional_str[]
 	INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
diff --git a/src/proto/typval.pro b/src/proto/typval.pro
index db8f94e..10f6e67 100644
--- a/src/proto/typval.pro
+++ b/src/proto/typval.pro
@@ -14,6 +14,7 @@
 int check_for_string_arg(typval_T *args, int idx);
 int check_for_nonempty_string_arg(typval_T *args, int idx);
 int check_for_opt_string_arg(typval_T *args, int idx);
+int check_for_opt_string_or_none_arg(typval_T *args, int idx, int *is_none);
 int check_for_number_arg(typval_T *args, int idx);
 int check_for_opt_number_arg(typval_T *args, int idx);
 int check_for_float_or_nr_arg(typval_T *args, int idx);
diff --git a/src/strings.c b/src/strings.c
index c04cbe8..f1c8af8 100644
--- a/src/strings.c
+++ b/src/strings.c
@@ -1962,7 +1962,7 @@
 
     if (in_vim9script()
 	    && (check_for_string_arg(argvars, 0) == FAIL
-		|| check_for_opt_string_arg(argvars, 1) == FAIL
+		|| check_for_opt_string_or_none_arg(argvars, 1, NULL) == FAIL
 		|| (argvars[1].v_type != VAR_UNKNOWN
 		    && check_for_opt_number_arg(argvars, 2) == FAIL)))
 	return;
@@ -1971,26 +1971,24 @@
     if (head == NULL)
 	return;
 
-    if (check_for_opt_string_arg(argvars, 1) == FAIL)
+    if (check_for_opt_string_or_none_arg(argvars, 1, NULL) == FAIL)
 	return;
 
     if (argvars[1].v_type == VAR_STRING)
-    {
 	mask = tv_get_string_buf_chk(&argvars[1], buf2);
 
-	if (argvars[2].v_type != VAR_UNKNOWN)
-	{
-	    int	error = 0;
+    if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN)
+    {
+	int	error = 0;
 
-	    // leading or trailing characters to trim
-	    dir = (int)tv_get_number_chk(&argvars[2], &error);
-	    if (error)
-		return;
-	    if (dir < 0 || dir > 2)
-	    {
-		semsg(_(e_invalid_argument_str), tv_get_string(&argvars[2]));
-		return;
-	    }
+	// leading or trailing characters to trim
+	dir = (int)tv_get_number_chk(&argvars[2], &error);
+	if (error)
+	    return;
+	if (dir < 0 || dir > 2)
+	{
+	    semsg(_(e_invalid_argument_str), tv_get_string(&argvars[2]));
+	    return;
 	}
     }
 
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index d598966..85d1d22 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -2231,11 +2231,15 @@
   call assert_fails('eval trim("  vim  ", " ", [])', 'E745:')
   call assert_fails('eval trim("  vim  ", " ", -1)', 'E475:')
   call assert_fails('eval trim("  vim  ", " ", 3)', 'E475:')
-  call assert_fails('eval trim("  vim  ", 0)', 'E1174:')
+  call assert_fails('eval trim("  vim  ", 0)', 'E1393:')
 
   let chars = join(map(range(1, 0x20) + [0xa0], {n -> n->nr2char()}), '')
   call assert_equal("x", trim(chars . "x" . chars))
 
+  call assert_equal("x", trim(chars . "x" . chars, v:none, 0))
+  call assert_equal("x" . chars, trim(chars . "x" . chars, v:none, 1))
+  call assert_equal(chars . "x", trim(chars . "x" . chars, v:none, 2))
+
   call assert_fails('let c=trim([])', 'E730:')
 endfunc
 
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 1efc47a..206f97f 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -4786,7 +4786,7 @@
 
 def Test_trim()
   v9.CheckDefAndScriptFailure(['trim(["a"])'], ['E1013: Argument 1: type mismatch, expected string but got list<string>', 'E1174: String required for argument 1'])
-  v9.CheckDefAndScriptFailure(['trim("a", ["b"])'], ['E1013: Argument 2: type mismatch, expected string but got list<string>', 'E1174: String required for argument 2'])
+  v9.CheckDefAndScriptFailure(['trim("a", ["b"])'], ['E1013: Argument 2: type mismatch, expected string but got list<string>', 'E1393: String or v:none required for argument 2'])
   v9.CheckDefAndScriptFailure(['trim("a", "b", "c")'], ['E1013: Argument 3: type mismatch, expected number but got string', 'E1210: Number required for argument 3'])
   trim('')->assert_equal('')
   trim('', '')->assert_equal('')
diff --git a/src/typval.c b/src/typval.c
index 08dd231..b6371aa 100644
--- a/src/typval.c
+++ b/src/typval.c
@@ -451,6 +451,37 @@
 }
 
 /*
+ * Check for an optional string argument at 'idx', that can also be 'v:none' to
+ * use the default value.
+ *
+ * If 'is_none' is non-NULL it is set to 0 and updated to 1 when "args[idx]" is
+ * 'v:none'.
+ */
+    int
+check_for_opt_string_or_none_arg(typval_T *args, int idx, int *is_none)
+{
+    if (is_none != NULL)
+	*is_none = 0;
+
+    if (args[idx].v_type == VAR_UNKNOWN)
+	return OK;
+
+    if (args[idx].v_type == VAR_SPECIAL
+	    && args[idx].vval.v_number == VVAL_NONE)
+    {
+	if (is_none != NULL)
+	    *is_none = 1;
+	return OK;
+    }
+
+    if (args[idx].v_type == VAR_STRING)
+	return OK;
+
+    semsg(_(e_string_or_none_required_for_argument_nr), idx + 1);
+    return FAIL;
+}
+
+/*
  * Give an error and return FAIL unless "args[idx]" is a number.
  */
     int
diff --git a/src/version.c b/src/version.c
index 208d7a8..0896c64 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2040,
+/**/
     2039,
 /**/
     2038,