changeset 19742:810eee1b42e3 v8.2.0427

patch 8.2.0427: it is not possible to check for a typo in a feature name Commit: https://github.com/vim/vim/commit/7929651e05b081fe55e0e745725a7ad78c51be16 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Mar 22 16:17:14 2020 +0100 patch 8.2.0427: it is not possible to check for a typo in a feature name Problem: It is not possible to check for a typo in a feature name. Solution: Add an extra argument to has().
author Bram Moolenaar <Bram@vim.org>
date Sun, 22 Mar 2020 16:30:03 +0100
parents 350dcc40421f
children 456d9eee3c81
files runtime/doc/eval.txt src/evalfunc.c src/testdir/check.vim src/testdir/test_functions.vim src/version.c
diffstat 5 files changed, 1153 insertions(+), 457 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1,4 +1,4 @@
-*eval.txt*	For Vim version 8.2.  Last change: 2020 Mar 16
+*eval.txt*	For Vim version 8.2.  Last change: 2020 Mar 22
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -2515,7 +2515,7 @@ glob({expr} [, {nosuf} [, {list} [, {all
 glob2regpat({expr})		String	convert a glob pat into a search pat
 globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]])
 				String	do glob({expr}) for all dirs in {path}
-has({feature})			Number	|TRUE| if feature {feature} supported
+has({feature} [, {check}])	Number	|TRUE| if feature {feature} supported
 has_key({dict}, {key})		Number	|TRUE| if {dict} has entry {key}
 haslocaldir([{winnr} [, {tabnr}]])
 				Number	|TRUE| if the window executed |:lcd|
@@ -4358,8 +4358,8 @@ feedkeys({string} [, {mode}])				*feedke
 		'L'	Lowlevel input.  Only works for Unix or when using the
 			GUI. Keys are used as if they were coming from the
 			terminal.  Other flags are not used.  *E980*
-			When a CTRL-C interrupts it sets the internal
-			"got_int" flag.
+			When a CTRL-C interrupts and 't' is included it sets
+			the internal "got_int" flag.
 		'i'	Insert the string instead of appending (see above).
 		'x'	Execute commands until typeahead is empty.  This is
 			similar to using ":normal!".  You can call feedkeys()
@@ -5828,10 +5828,20 @@ globpath({path}, {expr} [, {nosuf} [, {l
 			GetExpr()->globpath(&rtp)
 <
 							*has()*
-has({feature})	The result is a Number, which is 1 if the feature {feature} is
-		supported, zero otherwise.  The {feature} argument is a
-		string.  See |feature-list| below.
+has({feature} [, {check}])
+		When {check} is omitted or is zero: The result is a Number,
+		which is 1 if the feature {feature} is supported, zero
+		otherwise.  The {feature} argument is a string, case is
+		ignored.  See |feature-list| below.
+
+		When {check} is present and not zero: The result is a Number,
+		which is 1 if the feature {feature} could ever be supported,
+		zero otherwise.  This is useful to check for a typo in
+		{feature}.  Keep in mind that an older Vim version will not
+		know about a feature added later.
+
 		Also see |exists()|.
+
 		Note that to skip code that has a syntax error when the
 		feature is not available, Vim may skip the rest of the line
 		and miss a following `endif`.  Therfore put the `endif` on a
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -566,7 +566,7 @@ static funcentry_T global_functions[] =
     {"glob",		1, 4, FEARG_1,	  ret_any,	f_glob},
     {"glob2regpat",	1, 1, FEARG_1,	  ret_string,	f_glob2regpat},
     {"globpath",	2, 5, FEARG_2,	  ret_any,	f_globpath},
-    {"has",		1, 1, 0,	  ret_number,	f_has},
+    {"has",		1, 2, 0,	  ret_number,	f_has},
     {"has_key",		2, 2, FEARG_1,	  ret_number,	f_has_key},
     {"haslocaldir",	0, 2, FEARG_1,	  ret_number,	f_haslocaldir},
     {"hasmapto",	1, 3, FEARG_1,	  ret_number,	f_hasmapto},
@@ -3357,551 +3357,1148 @@ f_has(typval_T *argvars, typval_T *rettv
 {
     int		i;
     char_u	*name;
+    int		x = FALSE;
     int		n = FALSE;
-    static char	*(has_list[]) =
-    {
+    typedef struct {
+	char *name;
+	short present;
+    } has_item_T;
+    static has_item_T has_list[] =
+    {
+	{"amiga",
 #ifdef AMIGA
-	"amiga",
-# ifdef FEAT_ARP
-	"arp",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"arp",
+#if defined(AMIGA) && defined(FEAT_ARP)
+		1
+#else
+		0
+#endif
+		},
+	{"beos",
 #ifdef __BEOS__
-	"beos",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"haiku",
 #ifdef __HAIKU__
-	"haiku",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"bsd",
 #if defined(BSD) && !defined(MACOS_X)
-	"bsd",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"hpux",
 #ifdef hpux
-	"hpux",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"linux",
 #ifdef __linux__
-	"linux",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"mac",		// Mac OS X (and, once, Mac OS Classic)
+#ifdef MACOS_X
+		1
+#else
+		0
+#endif
+		},
+	{"osx",		// Mac OS X
 #ifdef MACOS_X
-	"mac",		// Mac OS X (and, once, Mac OS Classic)
-	"osx",		// Mac OS X
-# ifdef MACOS_X_DARWIN
-	"macunix",	// Mac OS X, with the darwin feature
-	"osxdarwin",	// synonym for macunix
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"macunix",	// Mac OS X, with the darwin feature
+#if defined(MACOS_X) && defined(MACOS_X_DARWIN)
+		1
+#else
+		0
+#endif
+		},
+	{"osxdarwin",	// synonym for macunix
+#if defined(MACOS_X) && defined(MACOS_X_DARWIN)
+		1
+#else
+		0
+#endif
+		},
+	{"qnx",
 #ifdef __QNX__
-	"qnx",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"sun",
 #ifdef SUN_SYSTEM
-	"sun",
-#else
-	"moon",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"unix",
 #ifdef UNIX
-	"unix",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"vms",
 #ifdef VMS
-	"vms",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"win32",
 #ifdef MSWIN
-	"win32",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"win32unix",
 #if defined(UNIX) && defined(__CYGWIN__)
-	"win32unix",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"win64",
 #ifdef _WIN64
-	"win64",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"ebcdic",
 #ifdef EBCDIC
-	"ebcdic",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"fname_case",
 #ifndef CASE_INSENSITIVE_FILENAME
-	"fname_case",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"acl",
 #ifdef HAVE_ACL
-	"acl",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"arabic",
 #ifdef FEAT_ARABIC
-	"arabic",
-#endif
-	"autocmd",
+		1
+#else
+		0
+#endif
+		},
+	{"autocmd", 1},
+	{"autochdir",
 #ifdef FEAT_AUTOCHDIR
-	"autochdir",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"autoservername",
 #ifdef FEAT_AUTOSERVERNAME
-	"autoservername",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"balloon_eval",
 #ifdef FEAT_BEVAL_GUI
-	"balloon_eval",
-# ifndef FEAT_GUI_MSWIN // other GUIs always have multiline balloons
-	"balloon_multiline",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"balloon_multiline",
+#if defined(FEAT_BEVAL_GUI) && !defined(FEAT_GUI_MSWIN)
+			// MS-Windows requires runtime check, see below
+		1
+#else
+		0
+#endif
+		},
+	{"balloon_eval_term",
 #ifdef FEAT_BEVAL_TERM
-	"balloon_eval_term",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"builtin_terms",
 #if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS)
-	"builtin_terms",
-# ifdef ALL_BUILTIN_TCAPS
-	"all_builtin_terms",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"all_builtin_terms",
+#if defined(ALL_BUILTIN_TCAPS)
+		1
+#else
+		0
+#endif
+		},
+	{"browsefilter",
 #if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \
 	|| defined(FEAT_GUI_MSWIN) \
 	|| defined(FEAT_GUI_MOTIF))
-	"browsefilter",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"byte_offset",
 #ifdef FEAT_BYTEOFF
-	"byte_offset",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"channel",
 #ifdef FEAT_JOB_CHANNEL
-	"channel",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"cindent",
 #ifdef FEAT_CINDENT
-	"cindent",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"clientserver",
 #ifdef FEAT_CLIENTSERVER
-	"clientserver",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"clipboard",
 #ifdef FEAT_CLIPBOARD
-	"clipboard",
-#endif
-	"cmdline_compl",
-	"cmdline_hist",
-	"comments",
+		1
+#else
+		0
+#endif
+		},
+	{"cmdline_compl", 1},
+	{"cmdline_hist", 1},
+	{"comments", 1},
+	{"conceal",
 #ifdef FEAT_CONCEAL
-	"conceal",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"cryptv",
 #ifdef FEAT_CRYPT
-	"cryptv",
-	"crypt-blowfish",
-	"crypt-blowfish2",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"crypt-blowfish",
+#ifdef FEAT_CRYPT
+		1
+#else
+		0
+#endif
+		},
+	{"crypt-blowfish2",
+#ifdef FEAT_CRYPT
+		1
+#else
+		0
+#endif
+		},
+	{"cscope",
 #ifdef FEAT_CSCOPE
-	"cscope",
-#endif
-	"cursorbind",
+		1
+#else
+		0
+#endif
+		},
+	{"cursorbind", 1},
+	{"cursorshape",
 #ifdef CURSOR_SHAPE
-	"cursorshape",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"debug",
 #ifdef DEBUG
-	"debug",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"dialog_con",
 #ifdef FEAT_CON_DIALOG
-	"dialog_con",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"dialog_gui",
 #ifdef FEAT_GUI_DIALOG
-	"dialog_gui",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"diff",
 #ifdef FEAT_DIFF
-	"diff",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"digraphs",
 #ifdef FEAT_DIGRAPHS
-	"digraphs",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"directx",
 #ifdef FEAT_DIRECTX
-	"directx",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"dnd",
 #ifdef FEAT_DND
-	"dnd",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"emacs_tags",
 #ifdef FEAT_EMACS_TAGS
-	"emacs_tags",
-#endif
-	"eval",	    // always present, of course!
-	"ex_extra", // graduated feature
+		1
+#else
+		0
+#endif
+		},
+	{"eval", 1},		// always present, of course!
+	{"ex_extra", 1},	// graduated feature
+	{"extra_search",
 #ifdef FEAT_SEARCH_EXTRA
-	"extra_search",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"file_in_path",
 #ifdef FEAT_SEARCHPATH
-	"file_in_path",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"filterpipe",
 #if defined(FEAT_FILTERPIPE) && !defined(VIMDLL)
-	"filterpipe",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"find_in_path",
 #ifdef FEAT_FIND_ID
-	"find_in_path",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"float",
 #ifdef FEAT_FLOAT
-	"float",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"folding",
 #ifdef FEAT_FOLDING
-	"folding",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"footer",
 #ifdef FEAT_FOOTER
-	"footer",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"fork",
 #if !defined(USE_SYSTEM) && defined(UNIX)
-	"fork",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gettext",
 #ifdef FEAT_GETTEXT
-	"gettext",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gui",
 #ifdef FEAT_GUI
-	"gui",
-#endif
-#ifdef FEAT_GUI_ATHENA
-# ifdef FEAT_GUI_NEXTAW
-	"gui_neXtaw",
-# else
-	"gui_athena",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gui_neXtaw",
+#if defined(FEAT_GUI_ATHENA) && defined(FEAT_GUI_NEXTAW)
+		1
+#else
+		0
+#endif
+		},
+	{"gui_athena",
+#if defined(FEAT_GUI_ATHENA) && !defined(FEAT_GUI_NEXTAW)
+		1
+#else
+		0
+#endif
+		},
+	{"gui_gtk",
 #ifdef FEAT_GUI_GTK
-	"gui_gtk",
-# ifdef USE_GTK3
-	"gui_gtk3",
-# else
-	"gui_gtk2",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gui_gtk2",
+#if defined(FEAT_GUI_GTK) && !defined(USE_GTK3)
+		1
+#else
+		0
+#endif
+		},
+	{"gui_gtk3",
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK3)
+		1
+#else
+		0
+#endif
+		},
+	{"gui_gnome",
 #ifdef FEAT_GUI_GNOME
-	"gui_gnome",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gui_haiku",
 #ifdef FEAT_GUI_HAIKU
-	"gui_haiku",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gui_mac",
 #ifdef FEAT_GUI_MAC
-	"gui_mac",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gui_motif",
 #ifdef FEAT_GUI_MOTIF
-	"gui_motif",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gui_photon",
 #ifdef FEAT_GUI_PHOTON
-	"gui_photon",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"gui_win32",
 #ifdef FEAT_GUI_MSWIN
-	"gui_win32",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"iconv",
 #if defined(HAVE_ICONV_H) && defined(USE_ICONV)
-	"iconv",
-#endif
-	"insert_expand",
+		1
+#else
+		0
+#endif
+		},
+	{"insert_expand", 1},
+	{"job",
 #ifdef FEAT_JOB_CHANNEL
-	"job",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"jumplist",
 #ifdef FEAT_JUMPLIST
-	"jumplist",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"keymap",
 #ifdef FEAT_KEYMAP
-	"keymap",
-#endif
-	"lambda", // always with FEAT_EVAL, since 7.4.2120 with closure
+		1
+#else
+		0
+#endif
+		},
+	{"lambda", 1}, // always with FEAT_EVAL, since 7.4.2120 with closure
+	{"langmap",
 #ifdef FEAT_LANGMAP
-	"langmap",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"libcall",
 #ifdef FEAT_LIBCALL
-	"libcall",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"linebreak",
 #ifdef FEAT_LINEBREAK
-	"linebreak",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"lispindent",
 #ifdef FEAT_LISP
-	"lispindent",
-#endif
-	"listcmds",
-	"localmap",
-#ifdef FEAT_LUA
-# ifndef DYNAMIC_LUA
-	"lua",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"listcmds", 1},
+	{"localmap", 1},
+	{"lua",
+#if defined(FEAT_LUA) && !defined(DYNAMIC_LUA)
+		1
+#else
+		0
+#endif
+		},
+	{"menu",
 #ifdef FEAT_MENU
-	"menu",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"mksession",
 #ifdef FEAT_SESSION
-	"mksession",
-#endif
-	"modify_fname",
-	"mouse",
+		1
+#else
+		0
+#endif
+		},
+	{"modify_fname", 1},
+	{"mouse", 1},
+	{"mouseshape",
 #ifdef FEAT_MOUSESHAPE
-	"mouseshape",
-#endif
-#if defined(UNIX) || defined(VMS)
-# ifdef FEAT_MOUSE_DEC
-	"mouse_dec",
-# endif
-# ifdef FEAT_MOUSE_GPM
-	"mouse_gpm",
-# endif
-# ifdef FEAT_MOUSE_JSB
-	"mouse_jsbterm",
-# endif
-# ifdef FEAT_MOUSE_NET
-	"mouse_netterm",
-# endif
-# ifdef FEAT_MOUSE_PTERM
-	"mouse_pterm",
-# endif
-# ifdef FEAT_MOUSE_XTERM
-	"mouse_sgr",
-# endif
-# ifdef FEAT_SYSMOUSE
-	"mouse_sysmouse",
-# endif
-# ifdef FEAT_MOUSE_URXVT
-	"mouse_urxvt",
-# endif
-# ifdef FEAT_MOUSE_XTERM
-	"mouse_xterm",
-# endif
-#endif
-	"multi_byte",
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_dec",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_DEC)
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_gpm",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_GPM)
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_jsbterm",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_JSB)
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_netterm",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_NET)
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_pterm",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_PTERM)
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_sgr",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_XTERM)
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_sysmouse",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_SYSMOUSE)
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_urxvt",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_URXVT)
+		1
+#else
+		0
+#endif
+		},
+	{"mouse_xterm",
+#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_XTERM)
+		1
+#else
+		0
+#endif
+		},
+	{"multi_byte", 1},
+	{"multi_byte_ime",
 #ifdef FEAT_MBYTE_IME
-	"multi_byte_ime",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"multi_lang",
 #ifdef FEAT_MULTI_LANG
-	"multi_lang",
-#endif
-#ifdef FEAT_MZSCHEME
-#ifndef DYNAMIC_MZSCHEME
-	"mzscheme",
-#endif
-#endif
-	"num64",
+		1
+#else
+		0
+#endif
+		},
+	{"mzscheme",
+#if defined(FEAT_MZSCHEME) && !defined(DYNAMIC_MZSCHEME)
+		1
+#else
+		0
+#endif
+		},
+	{"num64", 1},
+	{"ole",
 #ifdef FEAT_OLE
-	"ole",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"packages",
 #ifdef FEAT_EVAL
-	"packages",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"path_extra",
 #ifdef FEAT_PATH_EXTRA
-	"path_extra",
-#endif
-#ifdef FEAT_PERL
-#ifndef DYNAMIC_PERL
-	"perl",
-#endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"perl",
+#if defined(FEAT_PERL) && !defined(DYNAMIC_PERL)
+		1
+#else
+		0
+#endif
+		},
+	{"persistent_undo",
 #ifdef FEAT_PERSISTENT_UNDO
-	"persistent_undo",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"python_compiled",
 #if defined(FEAT_PYTHON)
-	"python_compiled",
-# if defined(DYNAMIC_PYTHON)
-	"python_dynamic",
-# else
-	"python",
-	"pythonx",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"python_dynamic",
+#if defined(FEAT_PYTHON) && defined(DYNAMIC_PYTHON)
+		1
+#else
+		0
+#endif
+		},
+	{"python",
+#if defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON)
+		1
+#else
+		0
+#endif
+		},
+	{"pythonx",
+#if (defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON)) \
+	|| (defined(FEAT_PYTHON3) && !defined(DYNAMIC_PYTHON3))
+		1
+#else
+		0
+#endif
+		},
+	{"python3_compiled",
 #if defined(FEAT_PYTHON3)
-	"python3_compiled",
-# if defined(DYNAMIC_PYTHON3)
-	"python3_dynamic",
-# else
-	"python3",
-	"pythonx",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"python3_dynamic",
+#if defined(FEAT_PYTHON3) && defined(DYNAMIC_PYTHON3)
+		1
+#else
+		0
+#endif
+		},
+	{"python3",
+#if defined(FEAT_PYTHON3) && !defined(DYNAMIC_PYTHON3)
+		1
+#else
+		0
+#endif
+		},
+	{"popupwin",
 #ifdef FEAT_PROP_POPUP
-	"popupwin",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"postscript",
 #ifdef FEAT_POSTSCRIPT
-	"postscript",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"printer",
 #ifdef FEAT_PRINTER
-	"printer",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"profile",
 #ifdef FEAT_PROFILE
-	"profile",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"reltime",
 #ifdef FEAT_RELTIME
-	"reltime",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"quickfix",
 #ifdef FEAT_QUICKFIX
-	"quickfix",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"rightleft",
 #ifdef FEAT_RIGHTLEFT
-	"rightleft",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"ruby",
 #if defined(FEAT_RUBY) && !defined(DYNAMIC_RUBY)
-	"ruby",
-#endif
-	"scrollbind",
+		1
+#else
+		0
+#endif
+		},
+	{"scrollbind", 1},
+	{"showcmd",
 #ifdef FEAT_CMDL_INFO
-	"showcmd",
-	"cmdline_info",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"cmdline_info",
+#ifdef FEAT_CMDL_INFO
+		1
+#else
+		0
+#endif
+		},
+	{"signs",
 #ifdef FEAT_SIGNS
-	"signs",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"smartindent",
 #ifdef FEAT_SMARTINDENT
-	"smartindent",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"startuptime",
 #ifdef STARTUPTIME
-	"startuptime",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"statusline",
 #ifdef FEAT_STL_OPT
-	"statusline",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"netbeans_intg",
 #ifdef FEAT_NETBEANS_INTG
-	"netbeans_intg",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"sound",
 #ifdef FEAT_SOUND
-	"sound",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"spell",
 #ifdef FEAT_SPELL
-	"spell",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"syntax",
 #ifdef FEAT_SYN_HL
-	"syntax",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"system",
 #if defined(USE_SYSTEM) || !defined(UNIX)
-	"system",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"tag_binary",
 #ifdef FEAT_TAG_BINS
-	"tag_binary",
-#endif
-#ifdef FEAT_TCL
-# ifndef DYNAMIC_TCL
-	"tcl",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"tcl",
+#if defined(FEAT_TCL) && !defined(DYNAMIC_TCL)
+		1
+#else
+		0
+#endif
+		},
+	{"termguicolors",
 #ifdef FEAT_TERMGUICOLORS
-	"termguicolors",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"terminal",
 #if defined(FEAT_TERMINAL) && !defined(MSWIN)
-	"terminal",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"terminfo",
 #ifdef TERMINFO
-	"terminfo",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"termresponse",
 #ifdef FEAT_TERMRESPONSE
-	"termresponse",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"textobjects",
 #ifdef FEAT_TEXTOBJ
-	"textobjects",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"textprop",
 #ifdef FEAT_PROP_POPUP
-	"textprop",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"tgetent",
 #ifdef HAVE_TGETENT
-	"tgetent",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"timers",
 #ifdef FEAT_TIMERS
-	"timers",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"title",
 #ifdef FEAT_TITLE
-	"title",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"toolbar",
 #ifdef FEAT_TOOLBAR
-	"toolbar",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"unnamedplus",
 #if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
-	"unnamedplus",
-#endif
-	"user-commands",    // was accidentally included in 5.4
-	"user_commands",
+		1
+#else
+		0
+#endif
+		},
+	{"user-commands", 1},    // was accidentally included in 5.4
+	{"user_commands", 1},
+	{"vartabs",
 #ifdef FEAT_VARTABS
-	"vartabs",
-#endif
-	"vertsplit",
+		1
+#else
+		0
+#endif
+		},
+	{"vertsplit", 1},
+	{"viminfo",
 #ifdef FEAT_VIMINFO
-	"viminfo",
-#endif
-	"vimscript-1",
-	"vimscript-2",
-	"vimscript-3",
-	"vimscript-4",
-	"virtualedit",
-	"visual",
-	"visualextra",
-	"vreplace",
+		1
+#else
+		0
+#endif
+		},
+	{"vimscript-1", 1},
+	{"vimscript-2", 1},
+	{"vimscript-3", 1},
+	{"vimscript-4", 1},
+	{"virtualedit", 1},
+	{"visual", 1},
+	{"visualextra", 1},
+	{"vreplace", 1},
+	{"vtp",
 #ifdef FEAT_VTP
-	"vtp",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"wildignore",
 #ifdef FEAT_WILDIGN
-	"wildignore",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"wildmenu",
 #ifdef FEAT_WILDMENU
-	"wildmenu",
-#endif
-	"windows",
+		1
+#else
+		0
+#endif
+		},
+	{"windows", 1},
+	{"winaltkeys",
 #ifdef FEAT_WAK
-	"winaltkeys",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"writebackup",
 #ifdef FEAT_WRITEBACKUP
-	"writebackup",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"xim",
 #ifdef FEAT_XIM
-	"xim",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"xfontset",
 #ifdef FEAT_XFONTSET
-	"xfontset",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"xpm",
+#if defined(FEAT_XPM_W32) || defined(HAVE_XPM)
+		1
+#else
+		0
+#endif
+		},
+	{"xpm_w32",	// for backward compatibility
 #ifdef FEAT_XPM_W32
-	"xpm",
-	"xpm_w32",	// for backward compatibility
-#else
-# if defined(HAVE_XPM)
-	"xpm",
-# endif
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"xsmp",
 #ifdef USE_XSMP
-	"xsmp",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"xsmp_interact",
 #ifdef USE_XSMP_INTERACT
-	"xsmp_interact",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"xterm_clipboard",
 #ifdef FEAT_XCLIPBOARD
-	"xterm_clipboard",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"xterm_save",
 #ifdef FEAT_XTERM_SAVE
-	"xterm_save",
-#endif
+		1
+#else
+		0
+#endif
+		},
+	{"X11",
 #if defined(UNIX) && defined(FEAT_X11)
-	"X11",
-#endif
-	NULL
+		1
+#else
+		0
+#endif
+		},
+	{NULL, 0}
     };
 
     name = tv_get_string(&argvars[0]);
-    for (i = 0; has_list[i] != NULL; ++i)
-	if (STRICMP(name, has_list[i]) == 0)
+    for (i = 0; has_list[i].name != NULL; ++i)
+	if (STRICMP(name, has_list[i].name) == 0)
 	{
-	    n = TRUE;
+	    x = TRUE;
+	    n = has_list[i].present;
 	    break;
 	}
 
-    if (n == FALSE)
-    {
-	if (STRNICMP(name, "patch", 5) == 0)
-	{
-	    if (name[5] == '-'
-		    && STRLEN(name) >= 11
-		    && vim_isdigit(name[6])
-		    && vim_isdigit(name[8])
-		    && vim_isdigit(name[10]))
-	    {
-		int major = atoi((char *)name + 6);
-		int minor = atoi((char *)name + 8);
-
-		// Expect "patch-9.9.01234".
-		n = (major < VIM_VERSION_MAJOR
-		     || (major == VIM_VERSION_MAJOR
-			 && (minor < VIM_VERSION_MINOR
-			     || (minor == VIM_VERSION_MINOR
-				 && has_patch(atoi((char *)name + 10))))));
-	    }
-	    else
-		n = has_patch(atoi((char *)name + 5));
-	}
-	else if (STRICMP(name, "vim_starting") == 0)
-	    n = (starting != 0);
-	else if (STRICMP(name, "ttyin") == 0)
-	    n = mch_input_isatty();
-	else if (STRICMP(name, "ttyout") == 0)
-	    n = stdout_isatty;
-	else if (STRICMP(name, "multi_byte_encoding") == 0)
-	    n = has_mbyte;
+    // features also in has_list[] but sometimes enabled at runtime
+    if (x == TRUE && n == FALSE)
+    {
+	if (0)
+	    ;
 #if defined(FEAT_BEVAL) && defined(FEAT_GUI_MSWIN)
 	else if (STRICMP(name, "balloon_multiline") == 0)
 	    n = multiline_balloon_available();
 #endif
-#ifdef DYNAMIC_TCL
-	else if (STRICMP(name, "tcl") == 0)
-	    n = tcl_enabled(FALSE);
+#ifdef VIMDLL
+	else if (STRICMP(name, "filterpipe") == 0)
+	    n = gui.in_use || gui.starting;
 #endif
 #if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
 	else if (STRICMP(name, "iconv") == 0)
@@ -3915,9 +4512,9 @@ f_has(typval_T *argvars, typval_T *rettv
 	else if (STRICMP(name, "mzscheme") == 0)
 	    n = mzscheme_enabled(FALSE);
 #endif
-#ifdef DYNAMIC_RUBY
-	else if (STRICMP(name, "ruby") == 0)
-	    n = ruby_enabled(FALSE);
+#ifdef DYNAMIC_PERL
+	else if (STRICMP(name, "perl") == 0)
+	    n = perl_enabled(FALSE);
 #endif
 #ifdef DYNAMIC_PYTHON
 	else if (STRICMP(name, "python") == 0)
@@ -3944,53 +4541,129 @@ f_has(typval_T *argvars, typval_T *rettv
 # endif
 	}
 #endif
-#ifdef DYNAMIC_PERL
-	else if (STRICMP(name, "perl") == 0)
-	    n = perl_enabled(FALSE);
-#endif
-#ifdef FEAT_GUI
-	else if (STRICMP(name, "gui_running") == 0)
-	    n = (gui.in_use || gui.starting);
-# ifdef FEAT_BROWSE
-	else if (STRICMP(name, "browse") == 0)
-	    n = gui.in_use;	// gui_mch_browse() works when GUI is running
-# endif
-#endif
-#ifdef FEAT_SYN_HL
-	else if (STRICMP(name, "syntax_items") == 0)
-	    n = syntax_present(curwin);
-#endif
-#ifdef FEAT_VTP
-	else if (STRICMP(name, "vcon") == 0)
-	    n = is_term_win32() && has_vtp_working();
-#endif
-#ifdef FEAT_NETBEANS_INTG
-	else if (STRICMP(name, "netbeans_enabled") == 0)
-	    n = netbeans_active();
-#endif
-#ifdef FEAT_MOUSE_GPM
-	else if (STRICMP(name, "mouse_gpm_enabled") == 0)
-	    n = gpm_enabled();
+#ifdef DYNAMIC_RUBY
+	else if (STRICMP(name, "ruby") == 0)
+	    n = ruby_enabled(FALSE);
+#endif
+#ifdef DYNAMIC_TCL
+	else if (STRICMP(name, "tcl") == 0)
+	    n = tcl_enabled(FALSE);
 #endif
 #if defined(FEAT_TERMINAL) && defined(MSWIN)
 	else if (STRICMP(name, "terminal") == 0)
 	    n = terminal_enabled();
 #endif
+    }
+
+    // features not in has_list[]
+    if (x == FALSE)
+    {
+	if (STRNICMP(name, "patch", 5) == 0)
+	{
+	    x = TRUE;
+	    if (name[5] == '-'
+		    && STRLEN(name) >= 11
+		    && vim_isdigit(name[6])
+		    && vim_isdigit(name[8])
+		    && vim_isdigit(name[10]))
+	    {
+		int major = atoi((char *)name + 6);
+		int minor = atoi((char *)name + 8);
+
+		// Expect "patch-9.9.01234".
+		n = (major < VIM_VERSION_MAJOR
+		     || (major == VIM_VERSION_MAJOR
+			 && (minor < VIM_VERSION_MINOR
+			     || (minor == VIM_VERSION_MINOR
+				 && has_patch(atoi((char *)name + 10))))));
+	    }
+	    else
+		n = has_patch(atoi((char *)name + 5));
+	}
+	else if (STRICMP(name, "vim_starting") == 0)
+	{
+	    x = TRUE;
+	    n = (starting != 0);
+	}
+	else if (STRICMP(name, "ttyin") == 0)
+	{
+	    x = TRUE;
+	    n = mch_input_isatty();
+	}
+	else if (STRICMP(name, "ttyout") == 0)
+	{
+	    x = TRUE;
+	    n = stdout_isatty;
+	}
+	else if (STRICMP(name, "multi_byte_encoding") == 0)
+	{
+	    x = TRUE;
+	    n = has_mbyte;
+	}
+	else if (STRICMP(name, "gui_running") == 0)
+	{
+	    x = TRUE;
+#ifdef FEAT_GUI
+	    n = (gui.in_use || gui.starting);
+#endif
+	}
+	else if (STRICMP(name, "browse") == 0)
+	{
+	    x = TRUE;
+#if defined(FEAT_GUI) && defined(FEAT_BROWSE)
+	    n = gui.in_use;	// gui_mch_browse() works when GUI is running
+#endif
+	}
+	else if (STRICMP(name, "syntax_items") == 0)
+	{
+	    x = TRUE;
+#ifdef FEAT_SYN_HL
+	    n = syntax_present(curwin);
+#endif
+	}
+	else if (STRICMP(name, "vcon") == 0)
+	{
+	    x = TRUE;
+#ifdef FEAT_VTP
+	    n = is_term_win32() && has_vtp_working();
+#endif
+	}
+	else if (STRICMP(name, "netbeans_enabled") == 0)
+	{
+	    x = TRUE;
+#ifdef FEAT_NETBEANS_INTG
+	    n = netbeans_active();
+#endif
+	}
+	else if (STRICMP(name, "mouse_gpm_enabled") == 0)
+	{
+	    x = TRUE;
+#ifdef FEAT_MOUSE_GPM
+	    n = gpm_enabled();
+#endif
+	}
+	else if (STRICMP(name, "conpty") == 0)
+	{
+	    x = TRUE;
 #if defined(FEAT_TERMINAL) && defined(MSWIN)
-	else if (STRICMP(name, "conpty") == 0)
 	    n = use_conpty();
 #endif
+	}
+	else if (STRICMP(name, "clipboard_working") == 0)
+	{
+	    x = TRUE;
 #ifdef FEAT_CLIPBOARD
-	else if (STRICMP(name, "clipboard_working") == 0)
 	    n = clip_star.available;
 #endif
-#ifdef VIMDLL
-	else if (STRICMP(name, "filterpipe") == 0)
-	    n = gui.in_use || gui.starting;
-#endif
-    }
-
-    rettv->vval.v_number = n;
+	}
+    }
+
+    if (argvars[1].v_type != VAR_UNKNOWN && tv_get_number(&argvars[1]) != 0)
+	// return whether feature could ever be enabled
+	rettv->vval.v_number = x;
+    else
+	// return whether feature is enabled
+	rettv->vval.v_number = n;
 }
 
 /*
--- a/src/testdir/check.vim
+++ b/src/testdir/check.vim
@@ -6,6 +6,9 @@ command -nargs=1 MissingFeature throw 'S
 " Command to check for the presence of a feature.
 command -nargs=1 CheckFeature call CheckFeature(<f-args>)
 func CheckFeature(name)
+  if !has(a:name, 1)
+    throw 'Checking for non-existent feature ' .. a:name
+  endif
   if !has(a:name)
     MissingFeature a:name
   endif
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -20,6 +20,14 @@ func Test_00_bufexists()
   call assert_equal(0, bufexists('Xfoo'))
 endfunc
 
+func Test_has()
+  call assert_equal(1, has('eval'))
+  call assert_equal(1, has('eval', 1))
+
+  call assert_equal(0, has('nonexistent'))
+  call assert_equal(0, has('nonexistent', 1))
+endfunc
+
 func Test_empty()
   call assert_equal(1, empty(''))
   call assert_equal(0, empty('a'))
@@ -1586,7 +1594,7 @@ func Test_confirm()
   call assert_equal(2, a)
 
   " confirm() should return 0 when pressing CTRL-C.
-  call feedkeys("\<C-c>", 'L')
+  call feedkeys("\<C-C>", 'L')
   let a = confirm('Are you sure?', "&Yes\n&No")
   call assert_equal(0, a)
 
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    427,
+/**/
     426,
 /**/
     425,