[vim/vim] Support 'template string' ('string interpolation') (#4634)

489 views
Skip to first unread message

aiya000

unread,
Jul 8, 2019, 9:39:41โ€ฏAM7/8/19
to vim/vim, Subscribed

This is a version that omitted the '_' support.
(Please see #4491 (comment))

If Bram really want to '_', let's discuss again at #4491 :D


You can view, comment on, or merge this pull request online at:

ย ย https://github.com/vim/vim/pull/4634

Commit Summary

  • Make a test to string interpolations
  • Implement string-interpolations
  • Add a help to the string-interpolations

File Changes

Patch Links:

โ€”
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub

K.Takata

unread,
Jul 8, 2019, 10:02:43โ€ฏAM7/8/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In runtime/doc/message.txt:

> @@ -769,6 +769,7 @@ a user-defined command.
 This can only happen when changing the source code, when adding a command in
 src/ex_cmds.h.  The lookup table then needs to be updated, by running: >
 	make cmdidxs
+<

This should be removed.

K.Takata

unread,
Jul 8, 2019, 10:07:24โ€ฏAM7/8/19
to vim/vim, Subscribed

Please also check my comments in this commit: aiya000@e4e7d0d.

mattn

unread,
Jul 8, 2019, 10:21:24โ€ฏAM7/8/19
to vim/vim, Subscribed

@mattn commented on this pull request.


In runtime/doc/eval.txt:

> +'${}' into a string. For example
+>
+	" Embeding literals
+	echo $'I have ${10}'          " I have 10
+	echo $'I ${{"love": 1000}}'   " I {'love': 1000}
+<
+These are same as
+>
+	echo 'I have ' . string(10)
+	echo 'I ' . string({"love": 1000})
+<
+'string-interpolations' helps you to write complex string. Also _ is same as
+$. This is useful to avoid confusing a lot of repeated $.
+>
+	echo $'${10} ${20} ${30}'   " unreadable
+	echo _'${10} ${20} ${30}'   " readable!

should be removed.

mattn

unread,
Jul 8, 2019, 10:21:37โ€ฏAM7/8/19
to vim/vim, Subscribed

@mattn commented on this pull request.


In runtime/doc/eval.txt:

> +'string-interpolations' helps you to write complex string. Also _ is same as
+$. This is useful to avoid confusing a lot of repeated $.
+>
+	echo $'${10} ${20} ${30}'   " unreadable
+	echo _'${10} ${20} ${30}'   " readable!
+<
+Other rules:
+>
+	" Only strings or literal strings doesn't apply string()
+	echo $'I am a ${"vim"}'   " I am a vim
+	                          " not: I am a 'vim'
+	echo $"I am a ${'vim'}"   " I am a vim
+
+	" Only unscoped variables can omit {}
+	let x = 10
+	echo _'$x'  " 10

ditto

mattn

unread,
Jul 8, 2019, 10:21:44โ€ฏAM7/8/19
to vim/vim, Subscribed

@mattn commented on this pull request.


In runtime/doc/eval.txt:

> +	echo $'${10} ${20} ${30}'   " unreadable
+	echo _'${10} ${20} ${30}'   " readable!
+<
+Other rules:
+>
+	" Only strings or literal strings doesn't apply string()
+	echo $'I am a ${"vim"}'   " I am a vim
+	                          " not: I am a 'vim'
+	echo $"I am a ${'vim'}"   " I am a vim
+
+	" Only unscoped variables can omit {}
+	let x = 10
+	echo _'$x'  " 10
+
+	" In template-string, you can write '$' as $$
+	echo _'$$'  " $

ditto

mattn

unread,
Jul 8, 2019, 10:25:24โ€ฏAM7/8/19
to vim/vim, Subscribed

@mattn commented on this pull request.


In runtime/doc/eval.txt:

> +Other rules:
+>
+	" Only strings or literal strings doesn't apply string()
+	echo $'I am a ${"vim"}'   " I am a vim
+	                          " not: I am a 'vim'
+	echo $"I am a ${'vim'}"   " I am a vim
+
+	" Only unscoped variables can omit {}
+	let x = 10
+	echo _'$x'  " 10
+
+	" In template-string, you can write '$' as $$
+	echo _'$$'  " $
+
+	" Escaping in template (non-literal) string
+	echo _"${10} \\ \""   " 10 \ "

ditto


In runtime/doc/eval.txt:

> +	echo $"I am a ${'vim'}"   " I am a vim
+
+	" Only unscoped variables can omit {}
+	let x = 10
+	echo _'$x'  " 10
+
+	" In template-string, you can write '$' as $$
+	echo _'$$'  " $
+
+	" Escaping in template (non-literal) string
+	echo _"${10} \\ \""   " 10 \ "
+<
+Exceptions:
+>
+	echo $'${var'  " E1000: Unterminated template literal: $'${var'
+	echo _'... $ ...'  " E1000: Missing variable name on a $: $'... $ ...'

ditto


In src/eval.c:

> @@ -4569,6 +4613,39 @@ eval7(
     case '\'':	ret = get_lit_string_tv(arg, rettv, evaluate);
 		break;
 
+    /*
+     * Template string constant: $"string ${var}", $'literal string ${var}', _"", _''

Template string constant: $"string ${var}"


In src/eval.c:

> +    }
+
+    return FALSE;
+}
+
+/*
+ * NOTICE: x[-1] must be readable.
+ */
+    static int
+is_closing_double_quote(char_u* x)
+{
+    return (x[0] == '"') && (x[-1] != '\\');
+}
+
+/*
+ * Allocate a variable for a $'string ${var} $x', $"", _'', _"" constant.

Allocate a variable for a $'string ${var} $x', $"" constant.


In src/eval.c:

> +
+/*
+ * Parse a `template` string into a (non template) string.
+ *
+ * The result MUST BE `free`d later! (if it is not NULL.)
+ */
+    static char_u*
+parse_template(
+	char_u* (*enclose)(char_u*),
+	char_u* (*disclose)(char_u*),
+	char_u* (*fix_quote)(char_u),
+	int (*is_closing_quote)(char_u*),
+	char_u* template,
+	int evaluate)
+{
+    const size_t	QUOTES_LENGTH = 3;  // a number of strlen("$''"), strlen("$\"\""), strlen("_''"), or strlen("_\"\"")

// a number of strlen("$''"), strlen("$""")

aiya000

unread,
Jul 10, 2019, 1:27:58โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In runtime/doc/message.txt:

> @@ -769,6 +769,7 @@ a user-defined command.

 This can only happen when changing the source code, when adding a command in

 src/ex_cmds.h.  The lookup table then needs to be updated, by running: >

 	make cmdidxs

+<

done ๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

Reply to this email directly, view it on GitHub, or mute the thread.

aiya000

unread,
Jul 10, 2019, 1:31:20โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In runtime/doc/eval.txt:

> +'${}' into a string. For example

+>

+	" Embeding literals

+	echo $'I have ${10}'          " I have 10

+	echo $'I ${{"love": 1000}}'   " I {'love': 1000}

+<

+These are same as

+>

+	echo 'I have ' . string(10)

+	echo 'I ' . string({"love": 1000})

+<

+'string-interpolations' helps you to write complex string. Also _ is same as

+$. This is useful to avoid confusing a lot of repeated $.

+>

+	echo $'${10} ${20} ${30}'   " unreadable

+	echo _'${10} ${20} ${30}'   " readable!

done ๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 1:32:21โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In runtime/doc/eval.txt:

> +'string-interpolations' helps you to write complex string. Also _ is same as

+$. This is useful to avoid confusing a lot of repeated $.

+>

+	echo $'${10} ${20} ${30}'   " unreadable

+	echo _'${10} ${20} ${30}'   " readable!

+<

+Other rules:

+>

+	" Only strings or literal strings doesn't apply string()

+	echo $'I am a ${"vim"}'   " I am a vim

+	                          " not: I am a 'vim'

+	echo $"I am a ${'vim'}"   " I am a vim

+

+	" Only unscoped variables can omit {}

+	let x = 10

+	echo _'$x'  " 10

I replaced to $'x' ๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 1:32:36โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In runtime/doc/eval.txt:

> +	echo $'${10} ${20} ${30}'   " unreadable
+	echo _'${10} ${20} ${30}'   " readable!
+<
+Other rules:
+>
+	" Only strings or literal strings doesn't apply string()
+	echo $'I am a ${"vim"}'   " I am a vim
+	                          " not: I am a 'vim'
+	echo $"I am a ${'vim'}"   " I am a vim
+
+	" Only unscoped variables can omit {}
+	let x = 10
+	echo _'$x'  " 10
+
+	" In template-string, you can write '$' as $$
+	echo _'$$'  " $

I replaced to $'$$', thanks :D

aiya000

unread,
Jul 10, 2019, 1:33:04โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In runtime/doc/eval.txt:

> +Other rules:
+>
+	" Only strings or literal strings doesn't apply string()
+	echo $'I am a ${"vim"}'   " I am a vim
+	                          " not: I am a 'vim'
+	echo $"I am a ${'vim'}"   " I am a vim
+
+	" Only unscoped variables can omit {}
+	let x = 10
+	echo _'$x'  " 10
+
+	" In template-string, you can write '$' as $$
+	echo _'$$'  " $
+
+	" Escaping in template (non-literal) string
+	echo _"${10} \\ \""   " 10 \ "

I replaced to

	echo $"${10} \\ \""   " 10 \ "

aiya000

unread,
Jul 10, 2019, 1:33:54โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In runtime/doc/eval.txt:

> +	echo $"I am a ${'vim'}"   " I am a vim
+
+	" Only unscoped variables can omit {}
+	let x = 10
+	echo _'$x'  " 10
+
+	" In template-string, you can write '$' as $$
+	echo _'$$'  " $
+
+	" Escaping in template (non-literal) string
+	echo _"${10} \\ \""   " 10 \ "
+<
+Exceptions:
+>
+	echo $'${var'  " E1000: Unterminated template literal: $'${var'
+	echo _'... $ ...'  " E1000: Missing variable name on a $: $'... $ ...'

Thanks!

	echo $'... $ ...'  " E1000: Missing variable name on a $: $'... $ ...'

aiya000

unread,
Jul 10, 2019, 1:35:09โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> @@ -4569,6 +4613,39 @@ eval7(

     case '\'':	ret = get_lit_string_tv(arg, rettv, evaluate);

 		break;

 

+    /*

+     * Template string constant: $"string ${var}", $'literal string ${var}', _"", _''

     * Template string constant: $"string ${var}", $'literal string ${var}'

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 1:35:54โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +    }

+

+    return FALSE;

+}

+

+/*

+ * NOTICE: x[-1] must be readable.

+ */

+    static int

+is_closing_double_quote(char_u* x)

+{

+    return (x[0] == '"') && (x[-1] != '\\');

+}

+

+/*

+ * Allocate a variable for a $'string ${var} $x', $"", _'', _"" constant.

๐Ÿ‘

 * Allocate a variable for a $'string ${var} $x' constant.

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 1:36:29โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +

+/*

+ * Parse a `template` string into a (non template) string.

+ *

+ * The result MUST BE `free`d later! (if it is not NULL.)

+ */

+    static char_u*

+parse_template(

+	char_u* (*enclose)(char_u*),

+	char_u* (*disclose)(char_u*),

+	char_u* (*fix_quote)(char_u),

+	int (*is_closing_quote)(char_u*),

+	char_u* template,

+	int evaluate)

+{

+    const size_t	QUOTES_LENGTH = 3;  // a number of strlen("$''"), strlen("$\"\""), strlen("_''"), or strlen("_\"\"")

๐Ÿ‘

    const size_t	QUOTES_LENGTH = 3;  // a number of strlen("$''") (or strlen("$\"\""))

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 1:39:20โ€ฏAM7/10/19
to vim/vim, Subscribed

@k-takata @mattn Thanks for your reviews!
I fixed reviewd terms ๐Ÿ‘

And I re-checked comments. Maybe all comments are fixed :D

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 2:00:58โ€ฏAM7/10/19
to vim/vim, Push

@aiya000 pushed 1 commit.

โ€”
You are receiving this because you are subscribed to this thread.

View it on GitHub

K.Takata

unread,
Jul 10, 2019, 2:11:10โ€ฏAM7/10/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In runtime/doc/eval.txt:

> @@ -1331,6 +1331,49 @@ to be doubled.  These two commands are equivalent: >
 	if a =~ '\s*'
 
 
+template-string					*template-string* *E1000*
+---------------
+
+$"string"	template string			*expr-$quote*
+$'string'	template literal string		*expr-$'*
+
+Note that either single quotes or double quotes are used.
+
+These strings supports 'string-interpolations'. That embeds any expressions by

s/supports/support/


In src/eval.c:

> +	char_u* (*enclose)(char_u*),
+	char_u* (*disclose)(char_u*),
+	char_u* (*fix_quote)(char_u),
+	int (*is_closing_quote)(char_u*),
+	char_u **arg,
+	typval_T *rettv,
+	int evaluate)
+{
+    char_u*	string;
+    char_u*	origin_string;
+    char_u*	template;
+
+    // NOTE: At here, "${}" of "${" "expr" "}", that called 'embedding', and "expr" called 'embedded'.
+
+    template = read_template_string(is_closing_quote, *arg);
+    if (template == NULL) {

This { should be the next line. (or should be removed, because this block is only one line.)


In src/eval.c:

> +/*
+ * Get a string between a opening "$'" and a closing "'" (or "$\"" and "\""),
+ * and check a taken string is not missing a closing "'".
+ *
+ * This result MUST BE `free`d later! (if it is not NULL.)
+ */
+    static char_u*
+read_template_string(int (*is_closing_quote)(char_u*), char_u* source)
+{
+    const size_t 	OPENING_QUOTE_LENGTH = 2;  // "$'"
+    char_u*		p;
+    char_u*		template;
+    size_t		template_length;
+
+    for (p = source + OPENING_QUOTE_LENGTH; !(is_closing_quote(p) || *p == NUL); MB_PTR_ADV(p)) {}
+    if (*p == NUL) {

This { should be the next line.


In src/eval.c:

> +	}
+	else if (to_source[0] == '$')  // Do embed to $var
+	{
+	    size_t	embedding_start_length = embedded[finished]->is_embedded_literal
+		? LITERAL_EMBEDDING_START_LENGTH
+		: VAR_EMBEDDING_START_LENGTH;
+	    size_t	embedding_close_length = embedded[finished]->is_embedded_literal
+		? 1
+		: 0;
+
+	    memcpy(to_dest, embedded[finished]->stringified, STRLEN(embedded[finished]->stringified));
+	    mb_ptr_adv_nth(&to_source, embedding_start_length + STRLEN(embedded[finished]->expr) + embedding_close_length - 1);  // 1 is increased by for's effect later!
+	    mb_ptr_adv_nth(&to_dest, STRLEN(embedded[finished]->stringified) - 1);  // same
+	    ++finished;
+	    continue;
+	} else if (MB_PTR2LEN(to_source) > 1) {

else should be the next line.
{ should be the next of the else line.


In src/eval.c:

> +
+    // If entered with the closing quote of a template string into this function
+    if (whole_string[1] == NUL)
+    {
+	semsg(_("E1000: Unterminated template literal: %s"), template);
+	return -1;
+    }
+
+    p = whole_string + 1;  // Skip the head of '"' (or '\'')
+    is_closing_quote = expect_literal_string
+	? is_closing_single_quote
+	: is_closing_double_quote;
+
+    for (; !is_closing_quote(p); MB_PTR_ADV(p))
+    {
+	switch (*p) {

This { should be the next line.
Can we use if (*p == NUL)?


In src/eval.c:

> + * Does the taken char match /[a-zA-Z_]/ ?
+ */
+    static int
+is_sneak_case_char(char_u x)
+{
+    const char_u SNEAK_CHARS[] = {
+	'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+	'_'
+    };
+    const size_t	SNEAK_CHARS_SIZE = 27;
+    size_t		i;
+
+    for (i = 0; i < SNEAK_CHARS_SIZE; ++i)
+    {
+	if (SNEAK_CHARS[i] == x) {

This { should be the next line.

K.Takata

unread,
Jul 10, 2019, 2:30:20โ€ฏAM7/10/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

> @@ -203,6 +203,29 @@ static struct vimvar

     {VV_NAME("versionlong",	 VAR_NUMBER), VV_RO},

 };

 

+/*

+ * A type for template literals.

+ * e.g. "${some_literal}", "$var".

+ */

+typedef struct {

+    /*

+     * Does this have a form "${some_literal}" ?

+     * (if this is FALSE, this have a form "$var".)

+     */

+    int is_embedded_literal;

+

+    /*

+     * "some_literal" or "var" or else.

+     */

+    char_u* expr;

โฌ‡๏ธ Suggested change
-    char_u* expr;

+    char_u *expr;

* should be prepended before a variable. Same for other declarations.

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 3:35:15โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In runtime/doc/eval.txt:

> @@ -1331,6 +1331,49 @@ to be doubled.  These two commands are equivalent: >

 	if a =~ '\s*'

 

 

+template-string					*template-string* *E1000*

+---------------

+

+$"string"	template string			*expr-$quote*

+$'string'	template literal string		*expr-$'*

+

+Note that either single quotes or double quotes are used.

+

+These strings supports 'string-interpolations'. That embeds any expressions by

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 3:36:12โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +	}

+	else if (to_source[0] == '$')  // Do embed to $var

+	{

+	    size_t	embedding_start_length = embedded[finished]->is_embedded_literal

+		? LITERAL_EMBEDDING_START_LENGTH

+		: VAR_EMBEDDING_START_LENGTH;

+	    size_t	embedding_close_length = embedded[finished]->is_embedded_literal

+		? 1

+		: 0;

+

+	    memcpy(to_dest, embedded[finished]->stringified, STRLEN(embedded[finished]->stringified));

+	    mb_ptr_adv_nth(&to_source, embedding_start_length + STRLEN(embedded[finished]->expr) + embedding_close_length - 1);  // 1 is increased by for's effect later!

+	    mb_ptr_adv_nth(&to_dest, STRLEN(embedded[finished]->stringified) - 1);  // same

+	    ++finished;

+	    continue;

+	} else if (MB_PTR2LEN(to_source) > 1) {

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 3:36:49โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +
+    // If entered with the closing quote of a template string into this function
+    if (whole_string[1] == NUL)
+    {
+	semsg(_("E1000: Unterminated template literal: %s"), template);
+	return -1;
+    }
+
+    p = whole_string + 1;  // Skip the head of '"' (or '\'')
+    is_closing_quote = expect_literal_string
+	? is_closing_single_quote
+	: is_closing_double_quote;
+
+    for (; !is_closing_quote(p); MB_PTR_ADV(p))
+    {
+	switch (*p) {

!!
I fixed!

aiya000

unread,
Jul 10, 2019, 3:37:44โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> @@ -203,6 +203,29 @@ static struct vimvar
     {VV_NAME("versionlong",	 VAR_NUMBER), VV_RO},
 };
 
+/*
+ * A type for template literals.
+ * e.g. "${some_literal}", "$var".
+ */
+typedef struct {
+    /*
+     * Does this have a form "${some_literal}" ?
+     * (if this is FALSE, this have a form "$var".)
+     */
+    int is_embedded_literal;
+
+    /*
+     * "some_literal" or "var" or else.
+     */
+    char_u* expr;

Thanks a lot!
I did :%s/\v([a-zA-Z_]+)\* ([a-zA-Z_])/\1 *\2/gc :D

aiya000

unread,
Jul 10, 2019, 3:39:01โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +	char_u* (*enclose)(char_u*),

+	char_u* (*disclose)(char_u*),

+	char_u* (*fix_quote)(char_u),

+	int (*is_closing_quote)(char_u*),

+	char_u **arg,

+	typval_T *rettv,

+	int evaluate)

+{

+    char_u*	string;

+    char_u*	origin_string;

+    char_u*	template;

+

+    // NOTE: At here, "${}" of "${" "expr" "}", that called 'embedding', and "expr" called 'embedded'.

+

+    template = read_template_string(is_closing_quote, *arg);

+    if (template == NULL) {

๐Ÿ˜Ž ๐Ÿ‘
I did /\v\) \{/ and f s<CR> all!

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 3:39:08โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +/*

+ * Get a string between a opening "$'" and a closing "'" (or "$\"" and "\""),

+ * and check a taken string is not missing a closing "'".

+ *

+ * This result MUST BE `free`d later! (if it is not NULL.)

+ */

+    static char_u*

+read_template_string(int (*is_closing_quote)(char_u*), char_u* source)

+{

+    const size_t 	OPENING_QUOTE_LENGTH = 2;  // "$'"

+    char_u*		p;

+    char_u*		template;

+    size_t		template_length;

+

+    for (p = source + OPENING_QUOTE_LENGTH; !(is_closing_quote(p) || *p == NUL); MB_PTR_ADV(p)) {}

+    if (*p == NUL) {

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 3:39:19โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> + * Does the taken char match /[a-zA-Z_]/ ?

+ */

+    static int

+is_sneak_case_char(char_u x)

+{

+    const char_u SNEAK_CHARS[] = {

+	'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

+	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',

+	'_'

+    };

+    const size_t	SNEAK_CHARS_SIZE = 27;

+    size_t		i;

+

+    for (i = 0; i < SNEAK_CHARS_SIZE; ++i)

+    {

+	if (SNEAK_CHARS[i] == x) {

aiya000

unread,
Jul 10, 2019, 3:40:11โ€ฏAM7/10/19
to vim/vim, Push

@aiya000 pushed 1 commit.

  • 61db5d1 Resolve k-takata's reviews for help and eval.c

โ€”
You are receiving this because you are subscribed to this thread.

View it on GitHub

aiya000

unread,
Jul 10, 2019, 3:43:25โ€ฏAM7/10/19
to vim/vim, Subscribed

Appveyor says

gcc -c -I. -Iproto -DWIN32 -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -DHAVE_GETTEXT -DHAVE_LOCALE_H -DDYNAMIC_GETTEXT -DFEAT_CSCOPE -DFEAT_MBYTE_IME -DDYNAMIC_IME -DDYNAMIC_ICONV -pipe -march=i686 -Wall -O2 iscygpty.c -o obji686/iscygpty.o -U_WIN32_WINNT -D_WIN32_WINNT=0x0600 -DUSE_DYNFILEID -DENABLE_STUB_IMPL
gcc -I. -Iproto -DWIN32 -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -DHAVE_GETTEXT -DHAVE_LOCALE_H -DDYNAMIC_GETTEXT -DFEAT_CSCOPE -DFEAT_MBYTE_IME -DDYNAMIC_IME -DDYNAMIC_ICONV -pipe -march=i686 -Wall -O2 -s -municode -o vim.exe obji686/arabic.o obji686/autocmd.o obji686/beval.o obji686/blob.o obji686/blowfish.o obji686/buffer.o obji686/change.o obji686/charset.o obji686/crypt.o obji686/crypt_zip.o obji686/debugger.o obji686/dict.o obji686/diff.o obji686/digraph.o obji686/edit.o obji686/eval.o obji686/evalfunc.o obji686/ex_cmds.o obji686/ex_cmds2.o obji686/ex_docmd.o obji686/ex_eval.o obji686/ex_getln.o obji686/fileio.o obji686/findfile.o obji686/fold.o obji686/getchar.o obji686/hardcopy.o obji686/hashtab.o obji686/indent.o obji686/insexpand.o obji686/json.o obji686/list.o obji686/main.o obji686/mark.o obji686/memfile.o obji686/memline.o obji686/menu.o obji686/message.o obji686/misc1.o obji686/misc2.o obji686/move.o obji686/mbyte.o obji686/normal.o obji686/ops.o obji686/option.o obji686/os_mswin.o obji686/os_win32.o obji686/pathdef.o obji686/popupmnu.o obji686/popupwin.o obji686/quickfix.o obji686/regexp.o obji686/screen.o obji686/search.o obji686/sha256.o obji686/sign.o obji686/spell.o obji686/spellfile.o obji686/syntax.o obji686/tag.o obji686/term.o obji686/textprop.o obji686/ui.o obji686/undo.o obji686/usercmd.o obji686/userfunc.o obji686/version.o obji686/winclip.o obji686/window.o obji686/os_w32exe.o obji686/vimrc.o obji686/if_cscope.o obji686/xdiffi.o obji686/xemit.o obji686/xprepare.o obji686/xutils.o obji686/xhistogram.o obji686/xpatience.o obji686/iscygpty.o -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomdlg32 -lcomctl32 -lnetapi32 -lversion -lole32 -luuid      
obji686/popupwin.o:popupwin.c:(.text+0x2ee1): undefined reference to `ch_log'
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe: *** [Make_cyg_ming.mak:1022: vim.exe] Error 1

But I don't have any ideas about this.
Is this my problem? X)

โ€”
You are receiving this because you are subscribed to this thread.

K.Takata

unread,
Jul 10, 2019, 3:44:17โ€ฏAM7/10/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

> @@ -6115,10 +6119,9 @@ read_next_string_length(char_u* whole_string, int expect_literal_string, char_u*
 
     for (; !is_closing_quote(p); MB_PTR_ADV(p))
     {
-	switch (*p) {
-	    case NUL:
-		semsg(_("E115: Missing quote: %s"), template);
-		return -1;
+	if (*p == NUL) {

{ should be the next line.

โ€”

K.Takata

unread,
Jul 10, 2019, 3:47:29โ€ฏAM7/10/19
to vim/vim, Subscribed

But I don't have any ideas about this.
Is this my problem? X)

Maybe no.
ref: https://groups.google.com/d/msg/vim_dev/zGy72qFwvYc/ZN9tZUpKBAAJ

aiya000

unread,
Jul 10, 2019, 4:48:46โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> @@ -6115,10 +6119,9 @@ read_next_string_length(char_u* whole_string, int expect_literal_string, char_u*
 
     for (; !is_closing_quote(p); MB_PTR_ADV(p))
     {
-	switch (*p) {
-	    case NUL:
-		semsg(_("E115: Missing quote: %s"), template);
-		return -1;
+	if (*p == NUL) {

Oh my hands... lol
Thanks!

K.Takata

unread,
Jul 10, 2019, 5:51:28โ€ฏAM7/10/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

> + *
+ * The result MUST BE `free`d later! (if it is not NULL.)
+ */
+    static char_u*
+parse_template(
+	char_u* (*enclose)(char_u*),
+	char_u* (*disclose)(char_u*),
+	char_u* (*fix_quote)(char_u),
+	int (*is_closing_quote)(char_u*),
+	char_u *template,
+	int evaluate)
+{
+    const size_t	QUOTES_LENGTH = 3;  // a number of strlen("$''") (or strlen("$\"\""))
+    const size_t	OPNEING_QUOTE_LENGTH = 2;  // a number of strlen("$'")
+    size_t		i;
+    char_u*		result;

Could you move this * before the variable name?
(And there are similar cases remaining.)


In src/eval.c:

> + */
+    static char_u*
+parse_template(
+	char_u* (*enclose)(char_u*),
+	char_u* (*disclose)(char_u*),
+	char_u* (*fix_quote)(char_u),
+	int (*is_closing_quote)(char_u*),
+	char_u *template,
+	int evaluate)
+{
+    const size_t	QUOTES_LENGTH = 3;  // a number of strlen("$''") (or strlen("$\"\""))
+    const size_t	OPNEING_QUOTE_LENGTH = 2;  // a number of strlen("$'")
+    size_t		i;
+    char_u*		result;
+    size_t		embedded_size;  // a size of embedded
+    embedded_t**	embedded = read_embedded(template, &embedded_size);

Same for this **.


In src/eval.c:

> + */
+    static void
+escape_quotes(char_u** source, char_u* (*fix_quote)(char_u))
+{
+    char_u*	result = alloc_terminated(STRLEN(*source));
+    char_u*	to_dest = result;
+    char_u*	to_source = *source;
+
+    for (; *to_source != NUL; MB_PTR_ADV(to_dest), MB_PTR_ADV(to_source))
+    {
+	char_u*		fixed = fix_quote(*to_source);
+	size_t		fixed_size = STRLEN(fixed);
+
+	memcpy(to_dest, fixed, fixed_size);
+	free(fixed);
+	mb_ptr_adv_nth(&to_dest, fixed_size - 1);  // 1 incresed by next for's effect

s/incresed/increased/


In src/eval.c:

> +
+/*
+ * Get a string between a opening "$'" and a closing "'" (or "$\"" and "\""),
+ * and check a taken string is not missing a closing "'".
+ *
+ * This result MUST BE `free`d later! (if it is not NULL.)
+ */
+    static char_u*
+read_template_string(int (*is_closing_quote)(char_u*), char_u *source)
+{
+    const size_t 	OPENING_QUOTE_LENGTH = 2;  // "$'"
+    char_u*		p;
+    char_u*		template;
+    size_t		template_length;
+
+    for (p = source + OPENING_QUOTE_LENGTH; !(is_closing_quote(p) || *p == NUL); MB_PTR_ADV(p));

It might be better to move the last ; to the next line (with indent) to emphasize that this is an empty loop.


In src/eval.c:

> + */
+    static char_u*
+alloc_terminated(size_t size)
+{
+    char_u*	p = alloc(size + 1);
+    p[size] = NUL;
+    return p;
+}
+
+/*
+ * Allocate a `embedded_t*`.
+ */
+    static embedded_t*
+new_embedded(int is_embedded_literal, char_u *expr, size_t expr_size)
+{
+    embedded_t *x = alloc(sizeof(embedded_t));

ALLOC_ONE can be used.
s/alloc(sizeof(embedded_t))/ALLOC_ONE(embedded_t)/


In src/eval.c:

> +    if (template == NULL)
+    {
+	return FAIL;
+    }
+
+    // Parse a template string into a plain string
+    string = parse_template(enclose, disclose, fix_quote, is_closing_quote, template, evaluate);
+    if (string == NULL)
+    {
+	return FAIL;
+    }
+
+    // Eval the result (non template) string
+    origin_string = string;
+    eval1(&string, rettv, evaluate);
+    free(origin_string);

vim_free() should be better than free() in Vim's source code.


In src/eval.c:

> +
+    // Allow to refer -1 at later
+    if (p[0] == '$' && p[1] != '$')
+    {
+	++count;
+    }
+    MB_PTR_ADV(p);
+
+    for (; *p != NUL; MB_PTR_ADV(p))
+    {
+	// Is `p` an independent '$'? e.g. "x$x". not "$$x" nor "x$$".
+	if (p[-1] != '$' && p[0] == '$' && p[1] != '$')
+	{
+	    ++count;
+	    ++p;      // a part of "$"
+	    continue; // a part of "{" (incresing)

s/incresing/increasing/


In src/eval.c:

> +is_closing_double_quote(char_u *x)
+{
+    return (x[0] == '"') && (x[-1] != '\\');
+}
+
+/*
+ * Allocate a variable for a $'string ${var} $x' constant.
+ * Return OK or FAIL.
+ *
+ * enclose() encloses a content by '' (or "").
+ * fix_quote() fixes quotes ' to ''. this fixes a problem that string() stringifies {"x": 10} to {'x': 10}, ["a", "b"] to ['a', 'b'].
+ * is_closing_quote() checks a char is ' (or ").
+ */
+    static int
+get_template_string_tv(
+	char_u* (*enclose)(char_u*),

s/char_u* (/char_u *(/


In src/eval.c:

> +    for (; !is_closing_quote(p); MB_PTR_ADV(p))
+    {
+	if (*p == NUL)
+	{
+	    semsg(_("E115: Missing quote: %s"), template);
+	    return -1;
+	}
+    }
+
+    return p - whole_string;
+}
+
+/*
+ * Get a first "expr" of "${" "expr" "}" from `whole_embedding`.
+ *
+ * whole_embedding starts with "${": e.g. "${10}`", "${var} Hello :D`".

The two backquotes can be removed?


In src/eval.c:

> +		break;
+	    }
+	    case NUL:
+		semsg(_("E1000: Unterminated template literal: %s"), template);
+		return NULL;  // failed! a closing "}" couldn't be found.
+	}
+	++expr_length;
+    }
+
+    return new_embedded(TRUE, embedded_start, expr_length);
+}
+
+/*
+ * Get a first "var" of "$" "var" from `whole_embedding`.
+ *
+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".

The backquote can be removed?


In src/eval.c:

> +    static embedded_t*
+read_next_embedded_var(char_u *whole_embedding, char_u *template)
+{
+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;
+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'
+    char_u*		p;
+
+    if (!is_sneak_case_char(expr_start[0]))
+    {
+	semsg(_("E1000: Missing variable name on a $: %s"), template);
+	return NULL;
+    }
+
+    for (p = expr_start + 1; *p != NUL; MB_PTR_ADV(p))
+    {
+	if (!is_sneak_case_char(*p))

Does this mean that numbers cannot be used for a variable name?


In src/testdir/Make_all.mak:

> @@ -283,7 +283,8 @@ NEW_TESTS = \
 	test_xxd \
 	test_alot_latin \
 	test_alot_utf8 \
-	test_alot
+	test_alot \
+	test_string_interpolation

This list should be in the alphabetical order (except the last test_alot tests).

aiya000

unread,
Jul 10, 2019, 8:37:08โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> + *
+ * The result MUST BE `free`d later! (if it is not NULL.)
+ */
+    static char_u*
+parse_template(
+	char_u* (*enclose)(char_u*),
+	char_u* (*disclose)(char_u*),
+	char_u* (*fix_quote)(char_u),
+	int (*is_closing_quote)(char_u*),
+	char_u *template,
+	int evaluate)
+{
+    const size_t	QUOTES_LENGTH = 3;  // a number of strlen("$''") (or strlen("$\"\""))
+    const size_t	OPNEING_QUOTE_LENGTH = 2;  // a number of strlen("$'")
+    size_t		i;
+    char_u*		result;

Sorry, I didn't replace it.

I'll :%s/\v([a-zA-Z_]+)(\*+)([ ])([\(a-zA-Z_])/\1\3\2\4/gc!

aiya000

unread,
Jul 10, 2019, 8:37:21โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> + */

+    static char_u*

+parse_template(

+	char_u* (*enclose)(char_u*),

+	char_u* (*disclose)(char_u*),

+	char_u* (*fix_quote)(char_u),

+	int (*is_closing_quote)(char_u*),

+	char_u *template,

+	int evaluate)

+{

+    const size_t	QUOTES_LENGTH = 3;  // a number of strlen("$''") (or strlen("$\"\""))

+    const size_t	OPNEING_QUOTE_LENGTH = 2;  // a number of strlen("$'")

+    size_t		i;

+    char_u*		result;

+    size_t		embedded_size;  // a size of embedded

+    embedded_t**	embedded = read_embedded(template, &embedded_size);

I'll %s/\v([a-zA-Z_]+)(\*+)([ ])([\(a-zA-Z_])/\1\3\2\4/gc ๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 8:37:47โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> + */

+    static void

+escape_quotes(char_u** source, char_u* (*fix_quote)(char_u))

+{

+    char_u*	result = alloc_terminated(STRLEN(*source));

+    char_u*	to_dest = result;

+    char_u*	to_source = *source;

+

+    for (; *to_source != NUL; MB_PTR_ADV(to_dest), MB_PTR_ADV(to_source))

+    {

+	char_u*		fixed = fix_quote(*to_source);

+	size_t		fixed_size = STRLEN(fixed);

+

+	memcpy(to_dest, fixed, fixed_size);

+	free(fixed);

+	mb_ptr_adv_nth(&to_dest, fixed_size - 1);  // 1 incresed by next for's effect

I'll fix ๐Ÿ‘

aiya000

unread,
Jul 10, 2019, 8:39:31โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +
+/*
+ * Get a string between a opening "$'" and a closing "'" (or "$\"" and "\""),
+ * and check a taken string is not missing a closing "'".
+ *
+ * This result MUST BE `free`d later! (if it is not NULL.)
+ */
+    static char_u*
+read_template_string(int (*is_closing_quote)(char_u*), char_u *source)
+{
+    const size_t 	OPENING_QUOTE_LENGTH = 2;  // "$'"
+    char_u*		p;
+    char_u*		template;
+    size_t		template_length;
+
+    for (p = source + OPENING_QUOTE_LENGTH; !(is_closing_quote(p) || *p == NUL); MB_PTR_ADV(p));

I'll do it :D

aiya000

unread,
Jul 10, 2019, 8:40:24โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> + */

+    static char_u*

+alloc_terminated(size_t size)

+{

+    char_u*	p = alloc(size + 1);

+    p[size] = NUL;

+    return p;

+}

+

+/*

+ * Allocate a `embedded_t*`.

+ */

+    static embedded_t*

+new_embedded(int is_embedded_literal, char_u *expr, size_t expr_size)

+{

+    embedded_t *x = alloc(sizeof(embedded_t));

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 8:42:08โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +    if (template == NULL)

+    {

+	return FAIL;

+    }

+

+    // Parse a template string into a plain string

+    string = parse_template(enclose, disclose, fix_quote, is_closing_quote, template, evaluate);

+    if (string == NULL)

+    {

+	return FAIL;

+    }

+

+    // Eval the result (non template) string

+    origin_string = string;

+    eval1(&string, rettv, evaluate);

+    free(origin_string);

Great!
I'll :%s/\<free\>/vim_free/gc for my codes ๐Ÿ˜Ž

aiya000

unread,
Jul 10, 2019, 8:42:32โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +
+    // Allow to refer -1 at later
+    if (p[0] == '$' && p[1] != '$')
+    {
+	++count;
+    }
+    MB_PTR_ADV(p);
+
+    for (; *p != NUL; MB_PTR_ADV(p))
+    {
+	// Is `p` an independent '$'? e.g. "x$x". not "$$x" nor "x$$".
+	if (p[-1] != '$' && p[0] == '$' && p[1] != '$')
+	{
+	    ++count;
+	    ++p;      // a part of "$"
+	    continue; // a part of "{" (incresing)

I'll fix!

aiya000

unread,
Jul 10, 2019, 8:42:48โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +is_closing_double_quote(char_u *x)

+{

+    return (x[0] == '"') && (x[-1] != '\\');

+}

+

+/*

+ * Allocate a variable for a $'string ${var} $x' constant.

+ * Return OK or FAIL.

+ *

+ * enclose() encloses a content by '' (or "").

+ * fix_quote() fixes quotes ' to ''. this fixes a problem that string() stringifies {"x": 10} to {'x': 10}, ["a", "b"] to ['a', 'b'].

+ * is_closing_quote() checks a char is ' (or ").

+ */

+    static int

+get_template_string_tv(

+	char_u* (*enclose)(char_u*),

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 8:46:56โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +    for (; !is_closing_quote(p); MB_PTR_ADV(p))
+    {
+	if (*p == NUL)
+	{
+	    semsg(_("E115: Missing quote: %s"), template);
+	    return -1;
+	}
+    }
+
+    return p - whole_string;
+}
+
+/*
+ * Get a first "expr" of "${" "expr" "}" from `whole_embedding`.
+ *
+ * whole_embedding starts with "${": e.g. "${10}`", "${var} Hello :D`".

I'll :%s/MUST BE freed/MUST BE EXECUTED BY free()/gc, and I'll search via /\v`[a-zA-Z_]``` and remove enclosing !

aiya000

unread,
Jul 10, 2019, 8:47:11โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +		break;

+	    }

+	    case NUL:

+		semsg(_("E1000: Unterminated template literal: %s"), template);

+		return NULL;  // failed! a closing "}" couldn't be found.

+	}

+	++expr_length;

+    }

+

+    return new_embedded(TRUE, embedded_start, expr_length);

+}

+

+/*

+ * Get a first "var" of "$" "var" from `whole_embedding`.

+ *

+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 9:12:03โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +    static embedded_t*
+read_next_embedded_var(char_u *whole_embedding, char_u *template)
+{
+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;
+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'
+    char_u*		p;
+
+    if (!is_sneak_case_char(expr_start[0]))
+    {
+	semsg(_("E1000: Missing variable name on a $: %s"), template);
+	return NULL;
+    }
+
+    for (p = expr_start + 1; *p != NUL; MB_PTR_ADV(p))
+    {
+	if (!is_sneak_case_char(*p))

No! I don't intend x)

I'll add tests, And fix this!

aiya000

unread,
Jul 10, 2019, 9:13:32โ€ฏAM7/10/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/testdir/Make_all.mak:

> @@ -283,7 +283,8 @@ NEW_TESTS = \

 	test_xxd \

 	test_alot_latin \

 	test_alot_utf8 \

-	test_alot

+	test_alot \

+	test_string_interpolation

I'll fix this ๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 10, 2019, 9:14:09โ€ฏAM7/10/19
to vim/vim, Push

@aiya000 pushed 1 commit.

  • e47122f Resolve k-takata's reviews for help, tests, and eval.c

โ€”
You are receiving this because you are subscribed to this thread.

View it on GitHub

aiya000

unread,
Jul 10, 2019, 9:14:36โ€ฏAM7/10/19
to vim/vim, Subscribed

@k-takata Thanks a lot!
I might resolve problems ๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

K.Takata

unread,
Jul 10, 2019, 12:15:32โ€ฏPM7/10/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

> @@ -244,6 +267,28 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);
 
 static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
+static int get_template_string_tv(char_u *(*enclose)(char_u*), char_u *(*disclose)(char_u*), char_u *(*fix_quote)(char_u), int (*is_closing_quote)(char_u*), char_u **arg, typval_T *rettv, int evaluate);
+static char_u* disclose_quote(char_u *quoted);

Please adjust the place of *.


In src/eval.c:

> +is_started_with_quote(char_u *x)
+{
+    return
+	x[0] == '\'' ||
+	x[0] == '"';
+}
+
+/*
+ * This result MUST BE EXECUTED BY free() later!
+ */
+    static char_u*
+disclose_quote(char_u *quoted)
+{
+    const size_t	QUOTES_LENGTH = 2;  // strlen("''")
+    size_t		result_length = STRLEN(quoted) - QUOTES_LENGTH;
+    char_u*		result;

ditto


In src/eval.c:

> +    vim_free(template);
+
+    return OK;
+}
+
+/*
+ * Get a string between a opening "$'" and a closing "'" (or "$\"" and "\""),
+ * and check a taken string is not missing a closing "'".
+ *
+ * This result MUST BE EXECUTED BY free() later! (if it is not NULL.)
+ */
+    static char_u*
+read_template_string(int (*is_closing_quote)(char_u*), char_u *source)
+{
+    const size_t 	OPENING_QUOTE_LENGTH = 2;  // "$'"
+    char_u*		p;

ditto


In src/eval.c:

> +
+/*
+ * Replace ' to ".
+ * this fixes a string() problem that doesn't save string({"x": 10})'s ".
+ * (It is stringified to {'x': 10}.)
+ */
+    static void
+escape_quotes(char_u **source, char_u *(*fix_quote)(char_u))
+{
+    char_u	*result = alloc_terminated(STRLEN(*source));
+    char_u	*to_dest = result;
+    char_u	*to_source = *source;
+
+    for (; *to_source != NUL; MB_PTR_ADV(to_dest), MB_PTR_ADV(to_source))
+    {
+	char_u*		fixed = fix_quote(*to_source);

ditto


In src/eval.c:

> +    {
+	MB_PTR_ADV(*p);
+    }
+}
+
+/*
+ * Replace all "${...}" of dest by each actual values.
+ * source is a template string.
+ * By `read_embedded()` or else, source must be checked that have no unterminated embedding.
+ */
+    static void
+embed_embedded(int (*is_closing_quote)(char_u*), char_u **dest, char_u *source, embedded_t **embedded, size_t embedded_size)
+{
+    const size_t	LITERAL_EMBEDDING_START_LENGTH = 2;  // a number of "${"
+    const size_t	VAR_EMBEDDING_START_LENGTH = 1;  // a number of head "$"
+    char_u*		to_dest;

ditto


In src/eval.c:

> +/*
+ * Read all embedded from template,
+ * and check a passed template string is terminated.
+ *
+ * Return got an array of all embedded.
+ *
+ * This result MUST BE EXECUTED BY free() later! (if it is not NUL.)
+ */
+    static embedded_t**
+read_embedded(char_u *template, size_t *embedded_size)
+{
+    const size_t	LITERAL_EMBEDDING_START_LENGTH = 2;  // a number of "${"
+    const size_t	VAR_EMBEDDING_START_LENGTH = 1;  // a number of "$"
+    embedded_t	**embedded;
+    size_t		finished = 0;
+    char_u*		p;

ditto


In src/eval.c:

> +
+    return p - whole_string;
+}
+
+/*
+ * Get a first "expr" of "${" "expr" "}" from whole_embedding.
+ *
+ * whole_embedding starts with "${": e.g. "${10}'", "${var} Hello :D'".
+ *
+ * The result MUST BE EXECUTED BY free() later! (if it is not NULL.)
+ */
+    static embedded_t*
+read_next_embedded_lit(char_u *whole_embedding, char_u *template)
+{
+    const size_t	LITERAL_EMBEDDING_START_LENGTH = 2;  // "${"
+    char_u*		embedded_start = whole_embedding + LITERAL_EMBEDDING_START_LENGTH;

ditto


In src/eval.c:

> +    return new_embedded(TRUE, embedded_start, expr_length);
+}
+
+/*
+ * Get a first "var" of "$" "var" from whole_embedding.
+ *
+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".
+ * (like "$x[0]" and "$x.y" only parses the part of "$x".)
+ *
+ * The result MUST BE EXECUTED BY free() later! (if it is not NULL.)
+ */
+    static embedded_t*
+read_next_embedded_var(char_u *whole_embedding, char_u *template)
+{
+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;
+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'

ditto

K.Takata

unread,
Jul 11, 2019, 12:22:15โ€ฏAM7/11/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

> +		break;
+	    }
+	    case NUL:
+		semsg(_("E1000: Unterminated template literal: %s"), template);
+		return NULL;  // failed! a closing "}" couldn't be found.
+	}
+	++expr_length;
+    }
+
+    return new_embedded(TRUE, embedded_start, expr_length);
+}
+
+/*
+ * Get a first "var" of "$" "var" from `whole_embedding`.
+ *
+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".

I meant that "$var`" isn't a mistake of "$var"?

K.Takata

unread,
Jul 11, 2019, 12:30:39โ€ฏAM7/11/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

>   *
- * whole_embedding starts with "${": e.g. "${10}`", "${var} Hello :D`".
+ * whole_embedding starts with "${": e.g. "${10}'", "${var} Hello :D'".

Are the changes of this line really correct?
Shouldn't be "${10}" and "${var} Hello :D"?

K.Takata

unread,
Jul 11, 2019, 1:39:08โ€ฏAM7/11/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

> +/*
+ * Get a first "var" of "$" "var" from whole_embedding.
+ *
+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".
+ * (like "$x[0]" and "$x.y" only parses the part of "$x".)
+ *
+ * The result MUST BE EXECUTED BY free() later! (if it is not NULL.)
+ */
+    static embedded_t*
+read_next_embedded_var(char_u *whole_embedding, char_u *template)
+{
+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;
+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'
+    char_u*		p;
+
+    if (!is_sneak_case_head_char(expr_start[0]))

I wonder if we really need this function.
Isn't !ASCII_ISALPHA(expr_start[0]) && expr_start[0] != '_' enough?


In src/eval.c:

> +    static embedded_t*
+read_next_embedded_var(char_u *whole_embedding, char_u *template)
+{
+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;
+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'
+    char_u*		p;
+
+    if (!is_sneak_case_head_char(expr_start[0]))
+    {
+	semsg(_("E1000: Missing variable name on a $: %s"), template);
+	return NULL;
+    }
+
+    for (p = expr_start + 1; *p != NUL; MB_PTR_ADV(p))
+    {
+	if (!is_sneak_case_char(*p))

I wonder if we really need this function.
Isn't !ASCII_ISALNUM(*p) && *p != '_' enough?

aiya000

unread,
Jul 11, 2019, 8:14:05โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +		break;

+	    }

+	    case NUL:

+		semsg(_("E1000: Unterminated template literal: %s"), template);

+		return NULL;  // failed! a closing "}" couldn't be found.

+	}

+	++expr_length;

+    }

+

+    return new_embedded(TRUE, embedded_start, expr_length);

+}

+

+/*

+ * Get a first "var" of "$" "var" from `whole_embedding`.

+ *

+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".

Sorry, my reply was too meaningless x(
"$var` is not intended, but "$var'" is intended.

I replaced to

 * whole_embedding starts with "$": e.g. "$var", "$var'", "$v_i_m".

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 11, 2019, 8:16:20โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> @@ -244,6 +267,28 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);
 
 static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
+static int get_template_string_tv(char_u *(*enclose)(char_u*), char_u *(*disclose)(char_u*), char_u *(*fix_quote)(char_u), int (*is_closing_quote)(char_u*), char_u **arg, typval_T *rettv, int evaluate);
+static char_u* disclose_quote(char_u *quoted);

Sorry, what do you mean?
static char_u *disclose_quote(char_u *quoted); ?

aiya000

unread,
Jul 11, 2019, 8:16:39โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +is_started_with_quote(char_u *x)

+{

+    return

+	x[0] == '\'' ||

+	x[0] == '"';

+}

+

+/*

+ * This result MUST BE EXECUTED BY free() later!

+ */

+    static char_u*

+disclose_quote(char_u *quoted)

+{

+    const size_t	QUOTES_LENGTH = 2;  // strlen("''")

+    size_t		result_length = STRLEN(quoted) - QUOTES_LENGTH;

+    char_u*		result;

Oh x(
Fixed ๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 11, 2019, 8:20:27โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +    {
+	MB_PTR_ADV(*p);
+    }
+}
+
+/*
+ * Replace all "${...}" of dest by each actual values.
+ * source is a template string.
+ * By `read_embedded()` or else, source must be checked that have no unterminated embedding.
+ */
+    static void
+embed_embedded(int (*is_closing_quote)(char_u*), char_u **dest, char_u *source, embedded_t **embedded, size_t embedded_size)
+{
+    const size_t	LITERAL_EMBEDDING_START_LENGTH = 2;  // a number of "${"
+    const size_t	VAR_EMBEDDING_START_LENGTH = 1;  // a number of head "$"
+    char_u*		to_dest;

wa!?
Sorry, I failed replacing...

I did :%s/\v([a-zA-Z_]+)(\*+)([ ]+)/\1\3\2/gc again!

aiya000

unread,
Jul 11, 2019, 8:20:41โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +/*

+ * Read all embedded from template,

+ * and check a passed template string is terminated.

+ *

+ * Return got an array of all embedded.

+ *

+ * This result MUST BE EXECUTED BY free() later! (if it is not NUL.)

+ */

+    static embedded_t**

+read_embedded(char_u *template, size_t *embedded_size)

+{

+    const size_t	LITERAL_EMBEDDING_START_LENGTH = 2;  // a number of "${"

+    const size_t	VAR_EMBEDDING_START_LENGTH = 1;  // a number of "$"

+    embedded_t	**embedded;

+    size_t		finished = 0;

+    char_u*		p;

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 11, 2019, 8:20:44โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +

+    return p - whole_string;

+}

+

+/*

+ * Get a first "expr" of "${" "expr" "}" from whole_embedding.

+ *

+ * whole_embedding starts with "${": e.g. "${10}'", "${var} Hello :D'".

+ *

+ * The result MUST BE EXECUTED BY free() later! (if it is not NULL.)

+ */

+    static embedded_t*

+read_next_embedded_lit(char_u *whole_embedding, char_u *template)

+{

+    const size_t	LITERAL_EMBEDDING_START_LENGTH = 2;  // "${"

+    char_u*		embedded_start = whole_embedding + LITERAL_EMBEDDING_START_LENGTH;

aiya000

unread,
Jul 11, 2019, 8:20:54โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +    return new_embedded(TRUE, embedded_start, expr_length);

+}

+

+/*

+ * Get a first "var" of "$" "var" from whole_embedding.

+ *

+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".

+ * (like "$x[0]" and "$x.y" only parses the part of "$x".)

+ *

+ * The result MUST BE EXECUTED BY free() later! (if it is not NULL.)

+ */

+    static embedded_t*

+read_next_embedded_var(char_u *whole_embedding, char_u *template)

+{

+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;

+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'

aiya000

unread,
Jul 11, 2019, 8:24:10โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

>   *
- * whole_embedding starts with "${": e.g. "${10}`", "${var} Hello :D`".
+ * whole_embedding starts with "${": e.g. "${10}'", "${var} Hello :D'".

Yes, I intended this :D
e.g. $'hello ${var} and ${10}' passes "${var} and ${10}' and "${10}' to this function!

aiya000

unread,
Jul 11, 2019, 8:25:44โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +    vim_free(template);

+

+    return OK;

+}

+

+/*

+ * Get a string between a opening "$'" and a closing "'" (or "$\"" and "\""),

+ * and check a taken string is not missing a closing "'".

+ *

+ * This result MUST BE EXECUTED BY free() later! (if it is not NULL.)

+ */

+    static char_u*

+read_template_string(int (*is_closing_quote)(char_u*), char_u *source)

+{

+    const size_t 	OPENING_QUOTE_LENGTH = 2;  // "$'"

+    char_u*		p;

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 11, 2019, 8:25:47โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +

+/*

+ * Replace ' to ".

+ * this fixes a string() problem that doesn't save string({"x": 10})'s ".

+ * (It is stringified to {'x': 10}.)

+ */

+    static void

+escape_quotes(char_u **source, char_u *(*fix_quote)(char_u))

+{

+    char_u	*result = alloc_terminated(STRLEN(*source));

+    char_u	*to_dest = result;

+    char_u	*to_source = *source;

+

+    for (; *to_source != NUL; MB_PTR_ADV(to_dest), MB_PTR_ADV(to_source))

+    {

+	char_u*		fixed = fix_quote(*to_source);

K.Takata

unread,
Jul 11, 2019, 8:27:12โ€ฏAM7/11/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

> @@ -244,6 +267,28 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);
 
 static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
+static int get_template_string_tv(char_u *(*enclose)(char_u*), char_u *(*disclose)(char_u*), char_u *(*fix_quote)(char_u), int (*is_closing_quote)(char_u*), char_u **arg, typval_T *rettv, int evaluate);
+static char_u* disclose_quote(char_u *quoted);

yes

aiya000

unread,
Jul 11, 2019, 8:33:22โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +/*

+ * Get a first "var" of "$" "var" from whole_embedding.

+ *

+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".

+ * (like "$x[0]" and "$x.y" only parses the part of "$x".)

+ *

+ * The result MUST BE EXECUTED BY free() later! (if it is not NULL.)

+ */

+    static embedded_t*

+read_next_embedded_var(char_u *whole_embedding, char_u *template)

+{

+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;

+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'

+    char_u*		p;

+

+    if (!is_sneak_case_head_char(expr_start[0]))

In my opinion, it allows "0aaa" and else (cases starts with [0-9]),
and doesn't allow _name (cases starts with _).
( Sorry if this is wrong x( )
But I want to allow only cases of [a-zA-Z_][a-zA-Z0-9_]+.

๐Ÿ˜„

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 11, 2019, 8:34:19โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +/*
+ * Get a first "var" of "$" "var" from whole_embedding.
+ *
+ * whole_embedding starts with "$": e.g. "$var`", "$v_i_m".
+ * (like "$x[0]" and "$x.y" only parses the part of "$x".)
+ *
+ * The result MUST BE EXECUTED BY free() later! (if it is not NULL.)
+ */
+    static embedded_t*
+read_next_embedded_var(char_u *whole_embedding, char_u *template)
+{
+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;
+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'
+    char_u*		p;
+
+    if (!is_sneak_case_head_char(expr_start[0]))

Sorry, I understood. this is not needed!
I used ASCII_ISALPHA instead of this function!

aiya000

unread,
Jul 11, 2019, 8:48:19โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +    static embedded_t*
+read_next_embedded_var(char_u *whole_embedding, char_u *template)
+{
+    const size_t	NUM_LAST_NON_SNEAK_CHAR = 1;
+    char_u*		expr_start = whole_embedding + 1;  // skip a head '$'
+    char_u*		p;
+
+    if (!is_sneak_case_head_char(expr_start[0]))
+    {
+	semsg(_("E1000: Missing variable name on a $: %s"), template);
+	return NULL;
+    }
+
+    for (p = expr_start + 1; *p != NUL; MB_PTR_ADV(p))
+    {
+	if (!is_sneak_case_char(*p))

Exactly xD
I fixed!

aiya000

unread,
Jul 11, 2019, 8:48:58โ€ฏAM7/11/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> @@ -244,6 +267,28 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);
 
 static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
+static int get_template_string_tv(char_u *(*enclose)(char_u*), char_u *(*disclose)(char_u*), char_u *(*fix_quote)(char_u), int (*is_closing_quote)(char_u*), char_u **arg, typval_T *rettv, int evaluate);
+static char_u* disclose_quote(char_u *quoted);

Thanks!
I fixed!

aiya000

unread,
Jul 11, 2019, 8:49:34โ€ฏAM7/11/19
to vim/vim, Push

@aiya000 pushed 1 commit.

  • a375ec8 Resolve k-takata's reviews for eval.c

โ€”
You are receiving this because you are subscribed to this thread.

View it on GitHub

aiya000

unread,
Jul 11, 2019, 8:49:54โ€ฏAM7/11/19
to vim/vim, Subscribed

@k-takata Thanks ๐Ÿ‘
I might fixed again :D

โ€”
You are receiving this because you are subscribed to this thread.

Codecov

unread,
Jul 11, 2019, 9:13:44โ€ฏAM7/11/19
to vim/vim, Subscribed

Codecov Report

Merging #4634 into master will decrease coverage by 0.16%.
The diff coverage is 1.32%.

Impacted file tree graph

@@            Coverage Diff             @@

##           master    #4634      +/-   ##

==========================================

- Coverage   81.33%   81.17%   -0.17%     

==========================================

  Files         111      111              

  Lines      144972   145278     +306     

==========================================

+ Hits       117918   117923       +5     

- Misses      27054    27355     +301
Impacted Files Coverage ฮ”
src/eval.c 81.44% <1.32%> (-5.07%) โฌ‡๏ธ
src/gui_gtk_x11.c 57.96% <0%> (-0.15%) โฌ‡๏ธ
src/ex_cmds2.c 88.29% <0%> (-0.12%) โฌ‡๏ธ
src/gui.c 63.73% <0%> (-0.06%) โฌ‡๏ธ
src/term.c 79.14% <0%> (-0.06%) โฌ‡๏ธ
src/channel.c 83.91% <0%> (-0.04%) โฌ‡๏ธ
src/normal.c 80.79% <0%> (-0.01%) โฌ‡๏ธ
src/version.c 91.89% <0%> (รธ) โฌ†๏ธ
src/popupwin.c 94.55% <0%> (+0.01%) โฌ†๏ธ
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
ฮ” = absolute <relative> (impact), รธ = not affected, ? = missing data
Powered by Codecov. Last update 7ba343e...a375ec8. Read the comment docs.

K.Takata

unread,
Jul 11, 2019, 11:53:47โ€ฏPM7/11/19
to vim/vim, Subscribed

According to the result of codecov, it seems that your test is not executed on CI.
You also need to add

source test_string_interpolation.vim

in src/testdir/test_alot.vim (or add test_string_interpolation.res in the NEW_TESTS_RES variable in src/testdir/Make_all.mak).

K.Takata

unread,
Jul 12, 2019, 12:09:05โ€ฏAM7/12/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/testdir/test_string_interpolation.vim:

> @@ -0,0 +1,123 @@

+" Test for string-interpolations $'${x} ${y.z}'

+

+scriptencoding utf-8

+

+func Test_string_interpolation()

+  call s:basic()

+  call s:appendix()

+  call s:illformed()

+endfunc

+

+func s:basic() abort

It might be better to make this function global. E.g. Test_string_interpolation_basic.
If it is global, you can run a specific test. See:
https://github.com/vim/vim/blob/e28cfb28126c07abd9e9ba04e36b3789433201b6/src/testdir/runtest.vim#L5-L7


In src/testdir/test_string_interpolation.vim:

> +  let F = {x -> x}

+  call assert_equal(string(F), $'${F}')

+

+  " Partial

+  call assert_equal(

+    \ string(function("has_key", [{"x": 10}])),

+    \ $'${function("has_key", [{"x": 10}])}'

+  \ )

+

+  " echo

+  echo $'${10}'

+  " let

+  let x = $'${10}'

+endfunc

+

+func s:appendix() abort

ditto


In src/testdir/test_string_interpolation.vim:

> +

+  " Escape '}'

+  call assert_equal("}", $'${"}"}')

+

+  " Independent '$'

+  call assert_equal('$', $'$$')

+

+  " Multi byte string

+  call assert_equal('ใ“ใ‚“ใซใกใฏ Vim', $'ใ“ใ‚“ใซใกใฏ ${"Vim"}')

+

+  " Double quoted

+  call assert_equal("10 \\ \"", $"${10} \\ \"")

+endfunc

+

+" These should be an exception, should not be a 'Segmentation fault'.

+func s:illformed() abort

ditto


In src/testdir/test_string_interpolation.vim:

> @@ -0,0 +1,123 @@

+" Test for string-interpolations $'${x} ${y.z}'

+

+scriptencoding utf-8

+

+func Test_string_interpolation()

This function is not needed, if you make all the static functions global.

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 12, 2019, 8:56:33โ€ฏAM7/12/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/testdir/test_string_interpolation.vim:

> @@ -0,0 +1,123 @@

+" Test for string-interpolations $'${x} ${y.z}'

+

+scriptencoding utf-8

+

+func Test_string_interpolation()

+  call s:basic()

+  call s:appendix()

+  call s:illformed()

+endfunc

+

+func s:basic() abort

I'll add it ๐Ÿ‘

aiya000

unread,
Jul 12, 2019, 8:56:50โ€ฏAM7/12/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/testdir/test_string_interpolation.vim:

> +

+  " Escape '}'

+  call assert_equal("}", $'${"}"}')

+

+  " Independent '$'

+  call assert_equal('$', $'$$')

+

+  " Multi byte string

+  call assert_equal('ใ“ใ‚“ใซใกใฏ Vim', $'ใ“ใ‚“ใซใกใฏ ${"Vim"}')

+

+  " Double quoted

+  call assert_equal("10 \\ \"", $"${10} \\ \"")

+endfunc

+

+" These should be an exception, should not be a 'Segmentation fault'.

+func s:illformed() abort

aiya000

unread,
Jul 12, 2019, 8:56:55โ€ฏAM7/12/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/testdir/test_string_interpolation.vim:

> @@ -0,0 +1,123 @@
+" Test for string-interpolations $'${x} ${y.z}'
+
+scriptencoding utf-8
+
+func Test_string_interpolation()

func Test_string_interpolation()
call s:basic()
call s:appendix()
call s:illformed()
endfunc

aiya000

unread,
Jul 12, 2019, 8:57:24โ€ฏAM7/12/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/testdir/test_string_interpolation.vim:

> +  let F = {x -> x}

+  call assert_equal(string(F), $'${F}')

+

+  " Partial

+  call assert_equal(

+    \ string(function("has_key", [{"x": 10}])),

+    \ $'${function("has_key", [{"x": 10}])}'

+  \ )

+

+  " echo

+  echo $'${10}'

+  " let

+  let x = $'${10}'

+endfunc

+

+func s:appendix() abort

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 14, 2019, 7:04:54โ€ฏAM7/14/19
to vim/vim, Push

@aiya000 pushed 7 commits.

  • 9e34580 Rename test_string_interpolation to test_template_string
  • 5660457 Add missing test_template_string to test_alot.vim
  • 11ddbfe Refine test_template_string
  • e2fe193 Add a test to empty template literals
  • 23c60e3 Add a help for empty template literals
  • 85d4e22 Add a test
  • 006c2fc Fix a test of empty template literals

โ€”
You are receiving this because you are subscribed to this thread.

View it on GitHub

aiya000

unread,
Jul 14, 2019, 7:05:23โ€ฏAM7/14/19
to vim/vim, Subscribed

@k-takata Sorry, Now I'm working to fix problems.
Please wait for re-checking ๐Ÿ™

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 14, 2019, 10:03:08โ€ฏAM7/14/19
to vim/vim, Subscribed

Closed #4634.

aiya000

unread,
Jul 14, 2019, 10:08:09โ€ฏAM7/14/19
to vim/vim, Subscribed

Reopened #4634.

aiya000

unread,
Jul 14, 2019, 10:10:02โ€ฏAM7/14/19
to vim/vim, Subscribed

Sorry, accidentally I closed this x(
I reopened!

aiya000

unread,
Jul 14, 2019, 10:23:47โ€ฏAM7/14/19
to vim/vim, Subscribed

According to the result of codecov, it seems that your test is not executed on CI.
You also need to add

source test_string_interpolation.vim
in src/testdir/test_alot.vim (or add test_string_interpolation.res in the NEW_TESTS_RES variable in src/testdir/Make_all.mak).

Thanks! I added! (as test_template_string.vim. please see below ๐Ÿ™)


I made these commits!

  • 4e17dd3 2019-07-12 21:58 Rename test_string_interpolation to test_template_string
  • 783f976 2019-07-12 21:58 Add missing test_template_string to test_alot.vim
  • 12b25b7 2019-07-12 22:43 Refine test_template_string
  • c5c709e 2019-07-14 16:13 Add a test to empty template literals
  • 78a4d2d 2019-07-14 16:13 Add a help for empty template literals
  • 1e7a03c 2019-07-14 18:33 Add a test
  • 8b81192 2019-07-14 18:33 Fix a test of empty template literals
  • 0706cb5 2019-07-14 22:21 Remove abort from tests
  • 27efec2 2019-07-14 23:00 Resolve tsuyoshi_cho's review

For detail:

  • 4e17dd3 2019-07-12 21:58 Rename test_string_interpolation to test_template_string
  • 783f976 2019-07-12 21:58 Add missing test_template_string to test_alot.vim
  • 12b25b7 2019-07-12 22:43 Refine test_template_string

โ˜๏ธ
These renames test_string_interpolation to test_template_string,
then adds it to Make_all.mak.

  • c5c709e 2019-07-14 16:13 Add a test to empty template literals
  • 78a4d2d 2019-07-14 16:13 Add a help for empty template literals
  • 8b81192 2019-07-14 18:33 Fix a test of empty template literals

โ˜๏ธ
mattn tell me a sensitive case ($'${}').
This adds it as a test, then fixes it.
Also adds a help.

  • 1e7a03c 2019-07-14 18:33 Add a test

โ˜๏ธ Adds a minimum test

+ call assert_equal('10 and me', $'$x and me')

  • 0706cb5 2019-07-14 22:21 Remove abort from tests

โ˜๏ธ Suddenly, I met two test failing after test_string_interpolation renamed to test_template_string.

This first is ๐Ÿ‘‡

From test_template_string.vim:

Executed Test_template_string_appendix() in   0.000358 seconds

Executed Test_template_string_basic() in   0.000271 seconds

Executed Test_template_string_illformed() in   0.000232 seconds

Executed abort in   0.000279 seconds

Executed abort in   0.000279 seconds

Executed abort in   0.000328 seconds

Executed 6 tests in   1.008782 seconds

3 FAILED:

Found errors in abort:

Caught exception in abort: Vim(call):E107: Missing parentheses: abort @ function RunTheTest, line 40

Found errors in abort:

Caught exception in abort: Vim(call):E107: Missing parentheses: abort @ function RunTheTest, line 40

Found errors in abort:

Caught exception in abort: Vim(call):E107: Missing parentheses: abort @ function RunTheTest, line 40

make: *** [Makefile:78: test_template_string] Error 1

This commit fixes it.

  • 27efec2 2019-07-14 23:00 Resolve tsuyoshi_cho's review

โ˜๏ธ The rest one is ๐Ÿ‘‡

Found errors in Test_template_string_basic():

function RunTheTest[40]..Test_template_string_basic line 3: Expected 'function(''function'')' but got 'function(''function'')ct\x01\x01'

This commit fixes it!


Thanks ๐Ÿ˜ƒ


@k-takata
Sorry for your waiting.
I might fix mentioned terms!

Please check again ๐Ÿ™‡

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 14, 2019, 10:35:28โ€ฏAM7/14/19
to vim/vim, Subscribed

wa!? test failure!?

aiya000

unread,
Jul 17, 2019, 5:01:54โ€ฏAM7/17/19
to vim/vim, Push

@aiya000 pushed 1 commit.

โ€”
You are receiving this because you are subscribed to this thread.

View it on GitHub

aiya000

unread,
Jul 20, 2019, 4:50:24โ€ฏAM7/20/19
to vim/vim, Push

@aiya000 pushed 3 commits.

  • 79e00f8 Fix Conditional jump or move depends on uninitialised value(s)
  • 1575c15 fixup! Refactor
  • aeba7f6 Fix 'Invalid read of size 1' and 'Invalid write of size 1'

aiya000

unread,
Jul 20, 2019, 5:29:07โ€ฏAM7/20/19
to vim/vim, Push

@aiya000 pushed 2 commits.

  • e2d7ca5 :+1: Fix definitely losing
  • 458d1d3 Don't check a test if it doesn't have

aiya000

unread,
Jul 20, 2019, 8:48:09โ€ฏAM7/20/19
to vim/vim, Subscribed

@k-takata Hi, thanks for your waiting :D

I did

  1. Pass tests for CI
  2. Fix memory leaks via valgrind

The 'current status' has a lot of 'possibly lost'.
But in my opinion, these are not my get_template_string_tv()'s memory leaks.
...Is this right?

I might completely fix problems!

I'll git-squash all commits if it is truth and if needed.

Thanks ๐Ÿ˜ƒ

โ€”
You are receiving this because you are subscribed to this thread.

K.Takata

unread,
Jul 20, 2019, 1:42:04โ€ฏPM7/20/19
to vim/vim, Subscribed

@k-takata commented on this pull request.


In src/eval.c:

> @@ -244,6 +267,28 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);

 

 static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);

 static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);

+static int get_template_string_tv(char_u *(*enclose)(char_u*), char_u *(*disclose)(char_u*), int (*should_be_escaped)(char_u), char_u *(*fix_quote)(char_u), int (*is_closing_quote)(char_u*), char_u **arg, typval_T *rettv, int evaluate);

+static int is_single_quote(char_u x);

+static int nothing_to_escape(char_u);

+static char_u *disclose_quote(char_u *quoted);

+static int is_started_with_quote(char_u *x);

+static void mb_ptr_adv_nth(char_u **p, size_t n);

+static char_u *read_template_string(int (*is_closing_quote)(char_u*), char_u *source);

+static char_u *enclose_by_single_quote(char_u *content);

+static char_u *enclose_by_double_quote(char_u *content);

+static int is_closing_single_quote(char_u *x);

+static int is_closing_double_quote(char_u *x);

+static char_u *duplicate_single_quote(char_u x);

+static char_u *dont_fix(char_u x);

+static char_u* parse_template(char_u *(*enclose)(char_u*), char_u *(*disclose)(char_u*), int (*should_be_escaped)(char_u), char_u *(*fix_quote)(char_u), int (*is_closing_quote)(char_u*), char_u *template, int evaluate);

s/char_u* /char */


In src/eval.c:

> +	{

+	    ++count;

+	    ++p;      // a part of "$"

+	    continue; // a part of "{" (increasing)

+	}

+    }

+

+    return count;

+}

+

+/*

+ * Allocate `size + 1` memory,

+ * also set the last and all to NUL.

+ */

+    static char_u*

+alloc_terminated(size_t size)

Is this function really needed?
Isn't this the same as (char_u*)alloc_clear(size + 1)?


In src/testdir/test_template_string.vim:

> +  call assert_equal("10 \\ \"", $"${10} \\ \"")

+endfunc

+

+" These should be an exception, should not be a 'Segmentation fault'.

+func Test_template_string_illformed()

+  try

+    let _ = $'missing closing quote

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E115/

+    " Good

+  endtry

+

+  try

+    let _ = $"missing closing double quote

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E115/

s/E115/E115:


In src/testdir/test_template_string.vim:

> +  catch /^Vim(let):E1000: Empty template literal/

+    " Good

+  endtry

+

+  try

+    let _ = $'I have ${nothing}'

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E121: Undefined variable: nothing$/

+    " Good

+  endtry

+

+  try

+    " syntax error in a embedding

+    let _ = $'missing closing of ${function(}'

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E119/

s/E119/E119:


In src/testdir/test_template_string.vim:

> +  " Independent '$'

+  call assert_equal('$', $'$$')

+

+  " Multi byte string

+  call assert_equal('ใ“ใ‚“ใซใกใฏ Vim', $'ใ“ใ‚“ใซใกใฏ ${"Vim"}')

+

+  " Double quoted

+  call assert_equal("10 \\ \"", $"${10} \\ \"")

+endfunc

+

+" These should be an exception, should not be a 'Segmentation fault'.

+func Test_template_string_illformed()

+  try

+    let _ = $'missing closing quote

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E115/

s/E115/E115:


In src/testdir/test_template_string.vim:

> +    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E115/

+    " Good

+  endtry

+

+  try

+    let _ = $"missing closing double quote

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E115/

+    " Good

+  endtry

+

+  try

+    let _ = $'$10'

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E1000: Missing variable name/

Error messages can be translated. This will fail when the language is not English.

aiya000

unread,
Jul 21, 2019, 3:46:53โ€ฏAM7/21/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> @@ -244,6 +267,28 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);

 

 static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);

 static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);

+static int get_template_string_tv(char_u *(*enclose)(char_u*), char_u *(*disclose)(char_u*), int (*should_be_escaped)(char_u), char_u *(*fix_quote)(char_u), int (*is_closing_quote)(char_u*), char_u **arg, typval_T *rettv, int evaluate);

+static int is_single_quote(char_u x);

+static int nothing_to_escape(char_u);

+static char_u *disclose_quote(char_u *quoted);

+static int is_started_with_quote(char_u *x);

+static void mb_ptr_adv_nth(char_u **p, size_t n);

+static char_u *read_template_string(int (*is_closing_quote)(char_u*), char_u *source);

+static char_u *enclose_by_single_quote(char_u *content);

+static char_u *enclose_by_double_quote(char_u *content);

+static int is_closing_single_quote(char_u *x);

+static int is_closing_double_quote(char_u *x);

+static char_u *duplicate_single_quote(char_u x);

+static char_u *dont_fix(char_u x);

+static char_u* parse_template(char_u *(*enclose)(char_u*), char_u *(*disclose)(char_u*), int (*should_be_escaped)(char_u), char_u *(*fix_quote)(char_u), int (*is_closing_quote)(char_u*), char_u *template, int evaluate);

๐Ÿ‘

aiya000

unread,
Jul 21, 2019, 3:51:05โ€ฏAM7/21/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/eval.c:

> +	{
+	    ++count;
+	    ++p;      // a part of "$"
+	    continue; // a part of "{" (increasing)
+	}
+    }
+
+    return count;
+}
+
+/*
+ * Allocate `size + 1` memory,
+ * also set the last and all to NUL.
+ */
+    static char_u*
+alloc_terminated(size_t size)

I'll remove it!

aiya000

unread,
Jul 21, 2019, 3:51:30โ€ฏAM7/21/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/testdir/test_template_string.vim:

> +  call assert_equal("10 \\ \"", $"${10} \\ \"")

+endfunc

+

+" These should be an exception, should not be a 'Segmentation fault'.

+func Test_template_string_illformed()

+  try

+    let _ = $'missing closing quote

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E115/

+    " Good

+  endtry

+

+  try

+    let _ = $"missing closing double quote

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E115/

๐Ÿ‘

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 21, 2019, 3:51:37โ€ฏAM7/21/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/testdir/test_template_string.vim:

> +  catch /^Vim(let):E1000: Empty template literal/

+    " Good

+  endtry

+

+  try

+    let _ = $'I have ${nothing}'

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E121: Undefined variable: nothing$/

+    " Good

+  endtry

+

+  try

+    " syntax error in a embedding

+    let _ = $'missing closing of ${function(}'

+    call assert_report('Should throw an exception.')

+  catch /^Vim(let):E119/

aiya000

unread,
Jul 21, 2019, 3:52:13โ€ฏAM7/21/19
to vim/vim, Subscribed

@aiya000 commented on this pull request.


In src/testdir/test_template_string.vim:

> +    call assert_report('Should throw an exception.')
+  catch /^Vim(let):E115/
+    " Good
+  endtry
+
+  try
+    let _ = $"missing closing double quote
+    call assert_report('Should throw an exception.')
+  catch /^Vim(let):E115/
+    " Good
+  endtry
+
+  try
+    let _ = $'$10'
+    call assert_report('Should throw an exception.')
+  catch /^Vim(let):E1000: Missing variable name/

Good! I'll remove messages!

aiya000

unread,
Jul 21, 2019, 3:53:12โ€ฏAM7/21/19
to vim/vim, Push

@aiya000 pushed 1 commit.

โ€”
You are receiving this because you are subscribed to this thread.

View it on GitHub

aiya000

unread,
Jul 21, 2019, 3:53:49โ€ฏAM7/21/19
to vim/vim, Subscribed

@k-takata Thank you as always ๐Ÿ˜‚
I might apply all review!

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Jul 26, 2019, 8:44:22โ€ฏAM7/26/19
to vim/vim, Subscribed

I did git-squash into only 3-commits :)

aiya000

unread,
Aug 10, 2019, 8:31:52โ€ฏAM8/10/19
to vim/vim, Subscribed

I love a number 1000.
Please don't change E1000 to any another number if Bram is ok ๐Ÿ™

If Bram doesn't agree this, I agree changing ๐Ÿ˜ญ

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Aug 11, 2019, 11:34:41โ€ฏPM8/11/19
to vim/vim, Push

@aiya000 pushed 1 commit.

  • 403ade6 fixup! Implement template-string

โ€”
You are receiving this because you are subscribed to this thread.

View it on GitHub

Bram Moolenaar

unread,
Sep 8, 2019, 9:37:07โ€ฏAM9/8/19
to vim/vim, Subscribed

I'm afraid that this is too much code for functionality that can also be done with string concatenation. Without adding $"" plugin authors can do the same thing, perhaps not that nice, but it's not worth so much code for something that is just a bit nicer syntax.

โ€”
You are receiving this because you are subscribed to this thread.

aiya000

unread,
Sep 8, 2019, 11:39:11โ€ฏPM9/8/19
to vim/vim, Subscribed

In my opinion, this is useful because $"" style strings is easier to read than concat style .. strings :)

This means (I think) $"" seems nearly to the result text!

aiya000

unread,
Sep 9, 2019, 6:00:59โ€ฏAM9/9/19
to vim/vim, Subscribed

@brammool
sorry, I misunderstood your message x(

How much do you expect code volume that you suppose?
(I'll do my best for it๐Ÿ‘๏ธ)

โ€”
You are receiving this because you are subscribed to this thread.

It is loading more messages.
0 new messages