patch 9.1.1340: cannot complete :filetype arguments

Problem:  cannot complete :filetype arguments (Phạm Bình An)
Solution: add :filetype ex command completion, add "filetypecmd"
          completion type for getcompletion()

fixes: #17165
closes: #17167

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 4dbd10e..f7d3ac8 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -53,6 +53,7 @@
 	    && xp->xp_context != EXPAND_FILES
 	    && xp->xp_context != EXPAND_FILES_IN_PATH
 	    && xp->xp_context != EXPAND_FILETYPE
+	    && xp->xp_context != EXPAND_FILETYPECMD
 	    && xp->xp_context != EXPAND_FINDFUNC
 	    && xp->xp_context != EXPAND_HELP
 	    && xp->xp_context != EXPAND_KEYMAP
@@ -2058,6 +2059,18 @@
 }
 #endif
 
+static enum
+{
+    EXP_FILETYPECMD_ALL,	// expand all :filetype values
+    EXP_FILETYPECMD_PLUGIN,	// expand plugin on off
+    EXP_FILETYPECMD_INDENT,	// expand indent on off
+    EXP_FILETYPECMD_ONOFF,	// expand on off
+} filetype_expand_what;
+
+#define EXPAND_FILETYPECMD_PLUGIN 0x01
+#define EXPAND_FILETYPECMD_INDENT 0x02
+#define EXPAND_FILETYPECMD_ONOFF  0x04
+
 #ifdef FEAT_EVAL
 static enum
 {
@@ -2144,6 +2157,53 @@
 #endif
 
 /*
+ * Set the completion context for the :filetype command. Always returns NULL.
+ */
+    static char_u *
+set_context_in_filetype_cmd(expand_T *xp, char_u *arg)
+{
+    char_u *p;
+    int    val = 0;
+
+    xp->xp_context = EXPAND_FILETYPECMD;
+    xp->xp_pattern = arg;
+    filetype_expand_what = EXP_FILETYPECMD_ALL;
+
+    p = skipwhite(arg);
+    if (*p == NUL)
+	return NULL;
+
+    for (;;)
+    {
+	if (STRNCMP(p, "plugin", 6) == 0)
+	{
+	    val |= EXPAND_FILETYPECMD_PLUGIN;
+	    p = skipwhite(p + 6);
+	    continue;
+	}
+	if (STRNCMP(p, "indent", 6) == 0)
+	{
+	    val |= EXPAND_FILETYPECMD_INDENT;
+	    p = skipwhite(p + 6);
+	    continue;
+	}
+	break;
+    }
+
+    if ((val & EXPAND_FILETYPECMD_PLUGIN) && (val & EXPAND_FILETYPECMD_INDENT))
+	filetype_expand_what = EXP_FILETYPECMD_ONOFF;
+    else if ((val & EXPAND_FILETYPECMD_PLUGIN))
+	filetype_expand_what = EXP_FILETYPECMD_INDENT;
+    else if ((val & EXPAND_FILETYPECMD_INDENT))
+	filetype_expand_what = EXP_FILETYPECMD_PLUGIN;
+
+    xp->xp_pattern = p;
+
+    return NULL;
+}
+
+
+/*
  * Set the completion context in 'xp' for command 'cmd' with index 'cmdidx'.
  * The argument to the command is 'arg' and the argument flags is 'argt'.
  * For user-defined commands and for environment variables, 'compl' has the
@@ -2515,6 +2575,8 @@
 	case CMD_scriptnames:
 	    return set_context_in_scriptnames_cmd(xp, arg);
 #endif
+	case CMD_filetype:
+	    return set_context_in_filetype_cmd(xp, arg);
 
 	default:
 	    break;
@@ -2949,6 +3011,29 @@
     return NULL;
 }
 
+/*
+ * Function given to ExpandGeneric() to obtain the possible arguments of the
+ * ":filetype {plugin,indent}" command.
+ */
+    static char_u *
+get_filetypecmd_arg(expand_T *xp UNUSED, int idx)
+{
+    char *opts_all[] = {"indent", "plugin", "on", "off"};
+    char *opts_plugin[] = {"plugin", "on", "off"};
+    char *opts_indent[] = {"indent", "on", "off"};
+    char *opts_onoff[] = {"on", "off"};
+
+    if (filetype_expand_what == EXP_FILETYPECMD_ALL && idx < 4)
+	return (char_u *)opts_all[idx];
+    if (filetype_expand_what == EXP_FILETYPECMD_PLUGIN && idx < 3)
+	return (char_u *)opts_plugin[idx];
+    if (filetype_expand_what == EXP_FILETYPECMD_INDENT && idx < 3)
+	return (char_u *)opts_indent[idx];
+    if (filetype_expand_what == EXP_FILETYPECMD_ONOFF && idx < 2)
+	return (char_u *)opts_onoff[idx];
+    return NULL;
+}
+
 #ifdef FEAT_EVAL
 /*
  * Function given to ExpandGeneric() to obtain the possible arguments of the
@@ -3040,6 +3125,7 @@
     {
 	{EXPAND_COMMANDS, get_command_name, FALSE, TRUE},
 	{EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE},
+	{EXPAND_FILETYPECMD, get_filetypecmd_arg, TRUE, TRUE},
 	{EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE},
 	{EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE},
 	{EXPAND_HISTORY, get_history_arg, TRUE, TRUE},
@@ -4295,6 +4381,8 @@
 								     &context);
 	    xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
 	}
+	if (xpc.xp_context == EXPAND_FILETYPECMD)
+	    filetype_expand_what = EXP_FILETYPECMD_ALL;
     }
 
     if (cmdline_fuzzy_completion_supported(&xpc))