changeset 15864:9e0154efac3a v8.1.0939

patch 8.1.0939: no completion for sign group names commit https://github.com/vim/vim/commit/3678f65d43d10b36dc62738aab2f341fa1e18a32 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Feb 17 14:50:25 2019 +0100 patch 8.1.0939: no completion for sign group names Problem: No completion for sign group names. Solution: Add completion for sign group names and buffer names. (Yegappan Lakshmanan, closes #3980)
author Bram Moolenaar <Bram@vim.org>
date Sun, 17 Feb 2019 15:00:06 +0100
parents 7c92dfb1bdd7
children bf72955a2d0f
files src/sign.c src/testdir/test_signs.vim src/version.c
diffstat 3 files changed, 153 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/src/sign.c
+++ b/src/sign.c
@@ -1767,20 +1767,65 @@ static enum
     EXP_SUBCMD,		// expand :sign sub-commands
     EXP_DEFINE,		// expand :sign define {name} args
     EXP_PLACE,		// expand :sign place {id} args
+    EXP_LIST,		// expand :sign place args
     EXP_UNPLACE,	// expand :sign unplace"
-    EXP_SIGN_NAMES	// expand with name of placed signs
+    EXP_SIGN_NAMES,	// expand with name of placed signs
+    EXP_SIGN_GROUPS	// expand with name of placed sign groups
 } expand_what;
 
 /*
+ * Return the n'th sign name (used for command line completion)
+ */
+    static char_u *
+get_nth_sign_name(int idx)
+{
+    int		current_idx;
+    sign_T	*sp;
+
+    // Complete with name of signs already defined
+    current_idx = 0;
+    for (sp = first_sign; sp != NULL; sp = sp->sn_next)
+	if (current_idx++ == idx)
+	    return sp->sn_name;
+    return NULL;
+}
+
+/*
+ * Return the n'th sign group name (used for command line completion)
+ */
+    static char_u *
+get_nth_sign_group_name(int idx)
+{
+    int		current_idx;
+    int		todo;
+    hashitem_T	*hi;
+    signgroup_T	*group;
+
+    // Complete with name of sign groups already defined
+    current_idx = 0;
+    todo = (int)sg_table.ht_used;
+    for (hi = sg_table.ht_array; todo > 0; ++hi)
+    {
+	if (!HASHITEM_EMPTY(hi))
+	{
+	    --todo;
+	    if (current_idx++ == idx)
+	    {
+		group = HI2SG(hi);
+		return group->sg_name;
+	    }
+	}
+    }
+    return NULL;
+}
+
+/*
  * Function given to ExpandGeneric() to obtain the sign command
  * expansion.
  */
     char_u *
 get_sign_name(expand_T *xp UNUSED, int idx)
 {
-    sign_T	*sp;
-    int		current_idx;
-
     switch (expand_what)
     {
     case EXP_SUBCMD:
@@ -1802,18 +1847,23 @@ get_sign_name(expand_T *xp UNUSED, int i
 	    };
 	    return (char_u *)place_arg[idx];
 	}
+    case EXP_LIST:
+	{
+	    char *list_arg[] =
+	    {
+		"group=", "file=", "buffer=", NULL
+	    };
+	    return (char_u *)list_arg[idx];
+	}
     case EXP_UNPLACE:
 	{
 	    char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
 	    return (char_u *)unplace_arg[idx];
 	}
     case EXP_SIGN_NAMES:
-	// Complete with name of signs already defined
-	current_idx = 0;
-	for (sp = first_sign; sp != NULL; sp = sp->sn_next)
-	    if (current_idx++ == idx)
-		return sp->sn_name;
-	return NULL;
+	return get_nth_sign_name(idx);
+    case EXP_SIGN_GROUPS:
+	return get_nth_sign_group_name(idx);
     default:
 	return NULL;
     }
@@ -1848,28 +1898,6 @@ set_context_in_sign_cmd(expand_T *xp, ch
     //		      |
     //		      begin_subcmd_args
     begin_subcmd_args = skipwhite(end_subcmd);
-    p = skiptowhite(begin_subcmd_args);
-    if (*p == NUL)
-    {
-	//
-	// Expand first argument of subcmd when possible.
-	// For ":jump {id}" and ":unplace {id}", we could
-	// possibly expand the ids of all signs already placed.
-	//
-	xp->xp_pattern = begin_subcmd_args;
-	switch (cmd_idx)
-	{
-	    case SIGNCMD_LIST:
-	    case SIGNCMD_UNDEFINE:
-		// :sign list <CTRL-D>
-		// :sign undefine <CTRL-D>
-		expand_what = EXP_SIGN_NAMES;
-		break;
-	    default:
-		xp->xp_context = EXPAND_NOTHING;
-	}
-	return;
-    }
 
     // expand last argument of subcmd
 
@@ -1878,6 +1906,7 @@ set_context_in_sign_cmd(expand_T *xp, ch
     //		    p
 
     // Loop until reaching last argument.
+    p = begin_subcmd_args;
     do
     {
 	p = skipwhite(p);
@@ -1900,7 +1929,19 @@ set_context_in_sign_cmd(expand_T *xp, ch
 		expand_what = EXP_DEFINE;
 		break;
 	    case SIGNCMD_PLACE:
-		expand_what = EXP_PLACE;
+		// List placed signs
+		if (VIM_ISDIGIT(*begin_subcmd_args))
+		    //   :sign place {id} {args}...
+		    expand_what = EXP_PLACE;
+		else
+		    //   :sign place {args}...
+		    expand_what = EXP_LIST;
+		break;
+	    case SIGNCMD_LIST:
+	    case SIGNCMD_UNDEFINE:
+		// :sign list <CTRL-D>
+		// :sign undefine <CTRL-D>
+		expand_what = EXP_SIGN_NAMES;
 		break;
 	    case SIGNCMD_JUMP:
 	    case SIGNCMD_UNPLACE:
@@ -1917,17 +1958,30 @@ set_context_in_sign_cmd(expand_T *xp, ch
 	switch (cmd_idx)
 	{
 	    case SIGNCMD_DEFINE:
-		if (STRNCMP(last, "texthl", p - last) == 0
-			|| STRNCMP(last, "linehl", p - last) == 0)
+		if (STRNCMP(last, "texthl", 6) == 0
+			|| STRNCMP(last, "linehl", 6) == 0)
 		    xp->xp_context = EXPAND_HIGHLIGHT;
-		else if (STRNCMP(last, "icon", p - last) == 0)
+		else if (STRNCMP(last, "icon", 4) == 0)
 		    xp->xp_context = EXPAND_FILES;
 		else
 		    xp->xp_context = EXPAND_NOTHING;
 		break;
 	    case SIGNCMD_PLACE:
-		if (STRNCMP(last, "name", p - last) == 0)
+		if (STRNCMP(last, "name", 4) == 0)
 		    expand_what = EXP_SIGN_NAMES;
+		else if (STRNCMP(last, "group", 5) == 0)
+		    expand_what = EXP_SIGN_GROUPS;
+		else if (STRNCMP(last, "file", 4) == 0)
+		    xp->xp_context = EXPAND_BUFFERS;
+		else
+		    xp->xp_context = EXPAND_NOTHING;
+		break;
+	    case SIGNCMD_UNPLACE:
+	    case SIGNCMD_JUMP:
+		if (STRNCMP(last, "group", 5) == 0)
+		    expand_what = EXP_SIGN_GROUPS;
+		else if (STRNCMP(last, "file", 4) == 0)
+		    xp->xp_context = EXPAND_BUFFERS;
 		else
 		    xp->xp_context = EXPAND_NOTHING;
 		break;
--- a/src/testdir/test_signs.vim
+++ b/src/testdir/test_signs.vim
@@ -210,13 +210,16 @@ func Test_sign_completion()
   call assert_equal('"sign define Sign linehl=SpellBad SpellCap ' .
 	      \ 'SpellLocal SpellRare', @:)
 
-  call writefile(['foo'], 'XsignOne')
-  call writefile(['bar'], 'XsignTwo')
+  call feedkeys(":sign define Sign texthl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign define Sign texthl=SpellBad SpellCap ' .
+	      \ 'SpellLocal SpellRare', @:)
+
+  call writefile(repeat(["Sun is shining"], 30), "XsignOne")
+  call writefile(repeat(["Sky is blue"], 30), "XsignTwo")
   call feedkeys(":sign define Sign icon=Xsig\<C-A>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sign define Sign icon=XsignOne XsignTwo', @:)
-  call delete('XsignOne')
-  call delete('XsignTwo')
 
+  " Test for completion of arguments to ':sign undefine'
   call feedkeys(":sign undefine \<C-A>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sign undefine Sign1 Sign2', @:)
 
@@ -227,17 +230,70 @@ func Test_sign_completion()
   call feedkeys(":sign place 1 name=\<C-A>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sign place 1 name=Sign1 Sign2', @:)
 
+  edit XsignOne
+  sign place 1 name=Sign1 line=5
+  sign place 1 name=Sign1 group=g1 line=10
+  edit XsignTwo
+  sign place 1 name=Sign2 group=g2 line=15
+
+  " Test for completion of group= and file= arguments to ':sign place'
+  call feedkeys(":sign place 1 name=Sign1 file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign place 1 name=Sign1 file=XsignOne XsignTwo', @:)
+  call feedkeys(":sign place 1 name=Sign1 group=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign place 1 name=Sign1 group=g1 g2', @:)
+
+  " Test for completion of arguments to 'sign place' without sign identifier
+  call feedkeys(":sign place \<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign place buffer= file= group=', @:)
+  call feedkeys(":sign place file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign place file=XsignOne XsignTwo', @:)
+  call feedkeys(":sign place group=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign place group=g1 g2', @:)
+  call feedkeys(":sign place group=g1 file=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign place group=g1 file=XsignOne XsignTwo', @:)
+
+  " Test for completion of arguments to ':sign unplace'
   call feedkeys(":sign unplace 1 \<C-A>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sign unplace 1 buffer= file= group=', @:)
+  call feedkeys(":sign unplace 1 file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign unplace 1 file=XsignOne XsignTwo', @:)
+  call feedkeys(":sign unplace 1 group=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign unplace 1 group=g1 g2', @:)
+  call feedkeys(":sign unplace 1 group=g2 file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign unplace 1 group=g2 file=XsignOne XsignTwo', @:)
 
+  " Test for completion of arguments to ':sign list'
   call feedkeys(":sign list \<C-A>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sign list Sign1 Sign2', @:)
 
+  " Test for completion of arguments to ':sign jump'
   call feedkeys(":sign jump 1 \<C-A>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sign jump 1 buffer= file= group=', @:)
+  call feedkeys(":sign jump 1 file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign jump 1 file=XsignOne XsignTwo', @:)
+  call feedkeys(":sign jump 1 group=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign jump 1 group=g1 g2', @:)
 
+  " Error cases
+  call feedkeys(":sign here\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"sign here', @:)
+  call feedkeys(":sign define Sign here=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"sign define Sign here=\<C-A>", @:)
+  call feedkeys(":sign place 1 here=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"sign place 1 here=\<C-A>", @:)
+  call feedkeys(":sign jump 1 here=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"sign jump 1 here=\<C-A>", @:)
+  call feedkeys(":sign here there\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"sign here there\<C-A>", @:)
+  call feedkeys(":sign here there=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"sign here there=\<C-A>", @:)
+
+  sign unplace * group=*
   sign undefine Sign1
   sign undefine Sign2
+  enew
+  call delete('XsignOne')
+  call delete('XsignTwo')
 endfunc
 
 func Test_sign_invalid_commands()
--- a/src/version.c
+++ b/src/version.c
@@ -780,6 +780,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    939,
+/**/
     938,
 /**/
     937,