changeset 27484:ee1019e59bef v8.2.4270

patch 8.2.4270: generating nv_cmdidxs.h requires building Vim twice Commit: https://github.com/vim/vim/commit/672776dbe8427876ef4bfce84520712df87b6eb1 Author: ichizok <gclient.gaap@gmail.com> Date: Mon Jan 31 12:27:18 2022 +0000 patch 8.2.4270: generating nv_cmdidxs.h requires building Vim twice Problem: Generating nv_cmdidxs.h requires building Vim twice. Solution: Move the table into a separate file and use a separate executable to extract the command characters. (Ozaki Kiichi, closes #9669)
author Bram Moolenaar <Bram@vim.org>
date Mon, 31 Jan 2022 13:30:05 +0100
parents e5134251817a
children 0226a849fd5b
files Filelist runtime/doc/builtin.txt runtime/doc/usr_41.txt src/Make_cyg_ming.mak src/Make_mvc.mak src/Make_vms.mms src/Makefile src/create_nvcmdidxs.c src/create_nvcmdidxs.vim src/evalfunc.c src/normal.c src/nv_cmds.h src/proto/normal.pro src/version.c
diffstat 14 files changed, 435 insertions(+), 410 deletions(-) [+]
line wrap: on
line diff
--- a/Filelist
+++ b/Filelist
@@ -114,6 +114,7 @@ SRC_ALL =	\
 		src/netbeans.c \
 		src/normal.c \
 		src/nv_cmdidxs.h \
+		src/nv_cmds.h \
 		src/ops.c \
 		src/option.c \
 		src/option.h \
@@ -444,6 +445,7 @@ SRC_UNIX =	\
 		src/configure \
 		src/configure.ac \
 		src/create_cmdidxs.vim \
+		src/create_nvcmdidxs.c \
 		src/create_nvcmdidxs.vim \
 		src/gui_at_fs.c \
 		src/gui_at_sb.c \
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -292,7 +292,6 @@ inputrestore()			Number	restore typeahea
 inputsave()			Number	save and clear typeahead
 inputsecret({prompt} [, {text}]) String	like input() but hiding the text
 insert({object}, {item} [, {idx}]) List	insert {item} in {object} [before {idx}]
-internal_get_nv_cmdchar({idx})	Number	command character at this index
 interrupt()			none	interrupt script execution
 invert({expr})			Number	bitwise invert
 isdirectory({directory})	Number	|TRUE| if {directory} is a directory
@@ -4618,11 +4617,6 @@ insert({object}, {item} [, {idx}])			*in
 
 		Can also be used as a |method|: >
 			mylist->insert(item)
-<
-						*internal_get_nv_cmdchar()*
-internal_get_nv_cmdchar({idx})
-		Return the normal/visual mode command character at the
-		specified index. To be used only during the Vim build process.
 
 interrupt()						*interrupt()*
 		Interrupt script execution.  It works more or less like the
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1110,7 +1110,6 @@ Testing:				    *test-functions*
 	assert_nobeep()		assert that a command does not cause a beep
 	assert_fails()		assert that a command fails
 	assert_report()		report a test failure
-	internal_get_nv_cmdchar()  normal/visual command character at an index
 	test_alloc_fail()	make memory allocation fail
 	test_autochdir()	enable 'autochdir' during startup
 	test_override()		test with Vim internal overrides
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -1145,17 +1145,17 @@ endif
 # If this fails because you don't have Vim yet, first build and install Vim
 # without changes.
 cmdidxs: ex_cmds.h
-	vim --clean -X --not-a-term -u create_cmdidxs.vim
+	vim --clean -N -X --not-a-term -u create_cmdidxs.vim -c quit
 
 # Run vim script to generate the normal/visual mode command lookup table.
 # This only needs to be run when a new normal/visual mode command has been
 # added.  If this fails because you don't have Vim yet:
-#   - change nv_cmds[] in normal.c to add the new normal/visual mode command.
-#   - build Vim
-#   - run "make nvcmdidxs" using the new Vim to generate nv_cmdidxs.h
-#   - rebuild Vim to use the newly generated nv_cmdidxs.h file.
-nvcmdidxs: normal.c
-	./$(TARGET) --clean -X --not-a-term -u create_nvcmdidxs.vim
+#   - change nv_cmds[] in nv_cmds.h to add the new normal/visual mode command.
+#   - run "make nvcmdidxs" to generate nv_cmdidxs.h
+nvcmdidxs: nv_cmds.h
+	$(CC) $(CFLAGS) -o create_nvcmdidxs.exe create_nvcmdidxs.c $(LIB)
+	vim --clean -N -X --not-a-term -u create_nvcmdidxs.vim -c quit
+	-$(DEL) create_nvcmdidxs.exe
 
 ###########################################################################
 INCL =	vim.h alloc.h ascii.h ex_cmds.h feature.h errors.h globals.h \
@@ -1219,7 +1219,7 @@ endif
 
 $(OUTDIR)/misc1.o: misc1.c $(INCL) version.h
 
-$(OUTDIR)/normal.o: normal.c $(INCL) nv_cmdidxs.h
+$(OUTDIR)/normal.o: normal.c $(INCL) nv_cmdidxs.h nv_cmds.h
 
 $(OUTDIR)/netbeans.o: netbeans.c $(INCL) version.h
 
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -1444,17 +1444,17 @@ clean: testclean
 # If this fails because you don't have Vim yet, first build and install Vim
 # without changes.
 cmdidxs: ex_cmds.h
-	vim --clean -X --not-a-term -u create_cmdidxs.vim
+	vim --clean -N -X --not-a-term -u create_cmdidxs.vim -c quit
 
 # Run vim script to generate the normal/visual mode command lookup table.
 # This only needs to be run when a new normal/visual mode command has been
 # added.  If this fails because you don't have Vim yet:
-#   - change nv_cmds[] in normal.c to add the new normal/visual mode command.
-#   - build Vim
-#   - run "make nvcmdidxs" using the new Vim to generate nv_cmdidxs.h
-#   - rebuild Vim to use the newly generated nv_cmdidxs.h file.
-nvcmdidxs: normal.c
-	.\$(VIM) --clean -X --not-a-term -u create_nvcmdidxs.vim
+#   - change nv_cmds[] in nv_cmds.h to add the new normal/visual mode command.
+#   - run "make nvcmdidxs" to generate nv_cmdidxs.h
+nvcmdidxs: nv_cmds.h
+	$(CC) /nologo -I. -Iproto -DNDEBUG create_nvcmdidxs.c -link -subsystem:$(SUBSYSTEM_TOOLS)
+	vim --clean -N -X --not-a-term -u create_nvcmdidxs.vim -c quit
+	-del create_nvcmdidxs.exe
 
 test:
 	cd testdir
@@ -1719,7 +1719,7 @@ lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).l
 
 $(OUTDIR)/channel.obj:	$(OUTDIR) channel.c $(INCL)
 
-$(OUTDIR)/normal.obj:	$(OUTDIR) normal.c  $(INCL) nv_cmdidxs.h
+$(OUTDIR)/normal.obj:	$(OUTDIR) normal.c  $(INCL) nv_cmdidxs.h nv_cmds.h
 
 $(OUTDIR)/option.obj:	$(OUTDIR) option.c  $(INCL) optiondefs.h
 
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -977,7 +977,7 @@ mbyte.obj : mbyte.c vim.h [.auto]config.
 normal.obj : normal.c vim.h [.auto]config.h feature.h os_unix.h \
  ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
- errors.h globals.h nv_cmdidxs.h
+ errors.h globals.h nv_cmdidxs.h nv_cmds.h
 ops.obj : ops.c vim.h [.auto]config.h feature.h os_unix.h   \
  ascii.h keymap.h termdefs.h macros.h structs.h regexp.h gui.h beval.h \
  [.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h
--- a/src/Makefile
+++ b/src/Makefile
@@ -2141,19 +2141,21 @@ autoconf:
 # This only needs to be run when a command name has been added or changed.
 # If this fails because you don't have Vim yet, first build and install Vim
 # without changes.
+# This requires a "vim" executable with the +eval feature.
 cmdidxs: ex_cmds.h
-	vim --clean -X --not-a-term -u create_cmdidxs.vim
+	vim --clean -N -X --not-a-term -u create_cmdidxs.vim -c quit
 
 # Run vim script to generate the normal/visual mode command lookup table.
 # This only needs to be run when a new normal/visual mode command has been
-# added.  If this fails because you don't have Vim yet:
-#   - change nv_cmds[] in normal.c to add the new normal/visual mode command.
-#   - build Vim
-#   - run "make nvcmdidxs" using the new Vim to generate nv_cmdidxs.h
-#   - rebuild Vim to use the newly generated nv_cmdidxs.h file.
-nvcmdidxs: normal.c
-	./$(VIMTARGET) --clean -X --not-a-term -u create_nvcmdidxs.vim
-
+# added.
+# This requires a "vim" executable with the +eval feature.
+# If this fails because you don't have Vim yet:
+#   - change nv_cmds[] in nv_cmds.h to add the new normal/visual mode command.
+#   - run "make nvcmdidxs" to generate nv_cmdidxs.h
+nvcmdidxs: auto/config.mk nv_cmds.h
+	$(CC) -I$(srcdir) $(ALL_CFLAGS) create_nvcmdidxs.c -o create_nvcmdidxs
+	vim --clean -N -X --not-a-term -u create_nvcmdidxs.vim -c quit
+	-rm -f create_nvcmdidxs
 
 # The normal command to compile a .c file to its .o file.
 # Without or with ALL_CFLAGS.
@@ -4012,7 +4014,7 @@ objects/move.o: move.c vim.h protodef.h 
 objects/normal.o: normal.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
- proto.h globals.h errors.h nv_cmdidxs.h
+ proto.h globals.h errors.h nv_cmdidxs.h nv_cmds.h
 objects/ops.o: ops.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
new file mode 100644
--- /dev/null
+++ b/src/create_nvcmdidxs.c
@@ -0,0 +1,38 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar et al.
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * create_nvcmdidxs.c: helper program for `make nvcmdidxs`
+ *
+ * This outputs the list of command characters from the nv_cmds table in
+ * decimal form, one per line.
+ */
+
+#include "vim.h"
+
+// Declare nv_cmds[].
+#include "nv_cmds.h"
+
+#include <stdio.h>
+
+int main(void)
+{
+    size_t i;
+
+    for (i = 0; i < NV_CMDS_SIZE; i++)
+    {
+	int cmdchar = nv_cmds[i];
+
+	// Special keys are negative, use the negated value for sorting.
+	if (cmdchar < 0)
+	    cmdchar = -cmdchar;
+	printf("%d\n", cmdchar);
+    }
+    return 0;
+}
--- a/src/create_nvcmdidxs.vim
+++ b/src/create_nvcmdidxs.vim
@@ -1,72 +1,60 @@
-vim9script
-
-# This script generates the table nv_cmd_idx[] which contains the index in
-# nv_cmds[] table (normal.c) for each of the command character supported in
-# normal/visual mode.
-# This is used to speed up the command lookup in nv_cmds[].
-#
-# Script should be run using "make nvcmdidxs", every time the nv_cmds[] table
-# in src/normal.c changes.
-
-def Create_nvcmdidxs_table()
-  var nv_cmdtbl: list<dict<number>> = []
+" This script generates the table nv_cmd_idx[] which contains the index in
+" nv_cmds[] table (normal.c) for each of the command character supported in
+" normal/visual mode.
+" This is used to speed up the command lookup in nv_cmds[].
+"
+" Script should be run using "make nvcmdidxs", every time the nv_cmds[] table
+" in src/nv_cmds.h changes.
+"
+" This is written in legacy Vim script so that it can be run by a slightly
+" older Vim version.
 
-  # Generate the table of normal/visual mode command characters and their
-  # corresponding index.
-  var idx: number = 0
-  var ch: number
-  while true
-    ch = internal_get_nv_cmdchar(idx)
-    if ch == -1
-      break
-    endif
-    add(nv_cmdtbl, {idx: idx, cmdchar: ch})
-    idx += 1
-  endwhile
+" Generate the table of normal/visual mode command characters and their
+" corresponding index.
+let cmd = 'create_nvcmdidxs'
+if has('unix')
+  let cmd = './' .. cmd
+endif
+let nv_cmdtbl = systemlist(cmd)->map({i, ch -> {'idx': i, 'cmdchar': ch}})
 
-  # sort the table by the command character
-  sort(nv_cmdtbl, (a, b) => a.cmdchar - b.cmdchar)
+" sort the table by the command character
+call sort(nv_cmdtbl, {a, b -> a.cmdchar - b.cmdchar})
 
-  # Compute the highest index upto which the command character can be directly
-  # used as an index.
-  var nv_max_linear: number = 0
-  for i in range(nv_cmdtbl->len())
-    if i != nv_cmdtbl[i].cmdchar
-      nv_max_linear = i - 1
-      break
-    endif
-  endfor
-
-  # Generate a header file with the table
-  var output: list<string> =<< trim END
-    /*
-     * Automatically generated code by the create_nvcmdidxs.vim script.
-     *
-     * Table giving the index in nv_cmds[] to lookup based on
-     * the command character.
-     */
+" Compute the highest index upto which the command character can be directly
+" used as an index.
+let nv_max_linear = 0
+for i in range(nv_cmdtbl->len())
+  if i != nv_cmdtbl[i].cmdchar
+    let nv_max_linear = i - 1
+    break
+  endif
+endfor
 
-    // nv_cmd_idx[<normal mode command character>] => nv_cmds[] index
-    static const unsigned short nv_cmd_idx[] =
-    {
-  END
+" Generate a header file with the table
+let output =<< trim END
+  /*
+   * Automatically generated code by the create_nvcmdidxs.vim script.
+   *
+   * Table giving the index in nv_cmds[] to lookup based on
+   * the command character.
+   */
 
-  # Add each command character in comment and the corresponding index
-  var tbl: list<string> = mapnew(nv_cmdtbl, (k, v) =>
-        '  /* ' .. printf('%5d', v.cmdchar) .. ' */ ' ..
-        printf('%3d', v.idx) .. ','
-    )
-  output += tbl
+  // nv_cmd_idx[<normal mode command character>] => nv_cmds[] index
+  static const unsigned short nv_cmd_idx[] =
+  {
+END
 
-  output += [ '};', '',
-              '// The highest index for which',
-              '// nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char]']
-  output += ['static const int nv_max_linear = ' .. nv_max_linear .. ';']
+" Add each command character in comment and the corresponding index
+let output += nv_cmdtbl->map({_, v ->
+      \ printf('  /* %5d */ %3d,', v.cmdchar, v.idx)})
 
-  writefile(output, "nv_cmdidxs.h")
-enddef
+let output += ['};', '',
+      \ '// The highest index for which',
+      \ '// nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char]']
 
-Create_nvcmdidxs_table()
+let output += ['static const int nv_max_linear = ' .. nv_max_linear .. ';']
+
+call writefile(output, "nv_cmdidxs.h")
 quit
 
-# vim: shiftwidth=2 sts=2 expandtab
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1735,8 +1735,6 @@ static funcentry_T global_functions[] =
 			ret_string,	    f_inputsecret},
     {"insert",		2, 3, FEARG_1,	    arg23_insert,
 			ret_first_arg,	    f_insert},
-    {"internal_get_nv_cmdchar",	1, 1, FEARG_1,    arg1_number,
-			ret_number,	    f_internal_get_nv_cmdchar},
     {"interrupt",	0, 0, 0,	    NULL,
 			ret_void,	    f_interrupt},
     {"invert",		1, 1, FEARG_1,	    arg1_number,
--- a/src/normal.c
+++ b/src/normal.c
@@ -1,6 +1,6 @@
 /* vi:set ts=8 sts=4 sw=4 noet:
  *
- * VIM - Vi IMproved	by Bram Moolenaar
+ * VIM - Vi IMproved	by Bram Moolenaar et al.
  *
  * Do ":help uganda"  in Vim to read copying and usage conditions.
  * Do ":help credits" in Vim to see a list of people who contributed.
@@ -127,320 +127,13 @@ static void	nv_drop(cmdarg_T *cap);
 #endif
 static void	nv_cursorhold(cmdarg_T *cap);
 
-#ifdef FEAT_GUI
-#define NV_VER_SCROLLBAR	nv_ver_scrollbar
-#define NV_HOR_SCROLLBAR	nv_hor_scrollbar
-#else
-#define NV_VER_SCROLLBAR nv_error
-#define NV_HOR_SCROLLBAR nv_error
-#endif
-
-#ifdef FEAT_GUI_TABLINE
-#define NV_TABLINE	nv_tabline
-#define NV_TABMENU	nv_tabmenu
-#else
-#define NV_TABLINE	nv_error
-#define NV_TABMENU	nv_error
-#endif
-
-#ifdef FEAT_NETBEANS_INTG
-#define NV_NBCMD	nv_nbcmd
-#else
-#define NV_NBCMD	nv_error
-#endif
-
-#ifdef FEAT_DND
-#define NV_DROP		nv_drop
-#else
-#define NV_DROP		nv_error
-#endif
-
-/*
- * Function to be called for a Normal or Visual mode command.
- * The argument is a cmdarg_T.
- */
-typedef void (*nv_func_T)(cmdarg_T *cap);
-
-// Values for cmd_flags.
-#define NV_NCH	    0x01	  // may need to get a second char
-#define NV_NCH_NOP  (0x02|NV_NCH) // get second char when no operator pending
-#define NV_NCH_ALW  (0x04|NV_NCH) // always get a second char
-#define NV_LANG	    0x08	// second char needs language adjustment
-
-#define NV_SS	    0x10	// may start selection
-#define NV_SSS	    0x20	// may start selection with shift modifier
-#define NV_STS	    0x40	// may stop selection without shift modif.
-#define NV_RL	    0x80	// 'rightleft' modifies command
-#define NV_KEEPREG  0x100	// don't clear regname
-#define NV_NCW	    0x200	// not allowed in command-line window
-
-/*
- * Generally speaking, every Normal mode command should either clear any
- * pending operator (with *clearop*()), or set the motion type variable
- * oap->motion_type.
- *
- * When a cursor motion command is made, it is marked as being a character or
- * line oriented motion.  Then, if an operator is in effect, the operation
- * becomes character or line oriented accordingly.
- */
-
-/*
- * This table contains one entry for every Normal or Visual mode command.
- * The order doesn't matter, this will be sorted by the create_nvcmdidx.vim
- * script to generate the nv_cmd_idx[] lookup table.
- * It is faster when all keys from zero to '~' are present.
- *
- * After changing the "nv_cmds" table:
- * 1. Build Vim with "make"
- * 2. Run "make nvcmdidxs" to re-generate the nv_cmdidxs.h file.
- * 3. Build Vim with "make" to use the newly generated index table.
- */
-static const struct nv_cmd
-{
-    int		cmd_char;	// (first) command character
-    nv_func_T   cmd_func;	// function for this command
-    short_u	cmd_flags;	// NV_ flags
-    short	cmd_arg;	// value for ca.arg
-} nv_cmds[] =
-{
-    {NUL,	nv_error,	0,			0},
-    {Ctrl_A,	nv_addsub,	0,			0},
-    {Ctrl_B,	nv_page,	NV_STS,			BACKWARD},
-    {Ctrl_C,	nv_esc,		0,			TRUE},
-    {Ctrl_D,	nv_halfpage,	0,			0},
-    {Ctrl_E,	nv_scroll_line,	0,			TRUE},
-    {Ctrl_F,	nv_page,	NV_STS,			FORWARD},
-    {Ctrl_G,	nv_ctrlg,	0,			0},
-    {Ctrl_H,	nv_ctrlh,	0,			0},
-    {Ctrl_I,	nv_pcmark,	0,			0},
-    {NL,	nv_down,	0,			FALSE},
-    {Ctrl_K,	nv_error,	0,			0},
-    {Ctrl_L,	nv_clear,	0,			0},
-    {CAR,	nv_down,	0,			TRUE},
-    {Ctrl_N,	nv_down,	NV_STS,			FALSE},
-    {Ctrl_O,	nv_ctrlo,	0,			0},
-    {Ctrl_P,	nv_up,		NV_STS,			FALSE},
-    {Ctrl_Q,	nv_visual,	0,			FALSE},
-    {Ctrl_R,	nv_redo_or_register, 0,			0},
-    {Ctrl_S,	nv_ignore,	0,			0},
-    {Ctrl_T,	nv_tagpop,	NV_NCW,			0},
-    {Ctrl_U,	nv_halfpage,	0,			0},
-    {Ctrl_V,	nv_visual,	0,			FALSE},
-    {Ctrl_W,	nv_window,	0,			0},
-    {Ctrl_X,	nv_addsub,	0,			0},
-    {Ctrl_Y,	nv_scroll_line,	0,			FALSE},
-    {Ctrl_Z,	nv_suspend,	0,			0},
-    {ESC,	nv_esc,		0,			FALSE},
-    {Ctrl_BSL,	nv_normal,	NV_NCH_ALW,		0},
-    {Ctrl_RSB,	nv_ident,	NV_NCW,			0},
-    {Ctrl_HAT,	nv_hat,		NV_NCW,			0},
-    {Ctrl__,	nv_error,	0,			0},
-    {' ',	nv_right,	0,			0},
-    {'!',	nv_operator,	0,			0},
-    {'"',	nv_regname,	NV_NCH_NOP|NV_KEEPREG,	0},
-    {'#',	nv_ident,	0,			0},
-    {'$',	nv_dollar,	0,			0},
-    {'%',	nv_percent,	0,			0},
-    {'&',	nv_optrans,	0,			0},
-    {'\'',	nv_gomark,	NV_NCH_ALW,		TRUE},
-    {'(',	nv_brace,	0,			BACKWARD},
-    {')',	nv_brace,	0,			FORWARD},
-    {'*',	nv_ident,	0,			0},
-    {'+',	nv_down,	0,			TRUE},
-    {',',	nv_csearch,	0,			TRUE},
-    {'-',	nv_up,		0,			TRUE},
-    {'.',	nv_dot,		NV_KEEPREG,		0},
-    {'/',	nv_search,	0,			FALSE},
-    {'0',	nv_beginline,	0,			0},
-    {'1',	nv_ignore,	0,			0},
-    {'2',	nv_ignore,	0,			0},
-    {'3',	nv_ignore,	0,			0},
-    {'4',	nv_ignore,	0,			0},
-    {'5',	nv_ignore,	0,			0},
-    {'6',	nv_ignore,	0,			0},
-    {'7',	nv_ignore,	0,			0},
-    {'8',	nv_ignore,	0,			0},
-    {'9',	nv_ignore,	0,			0},
-    {':',	nv_colon,	0,			0},
-    {';',	nv_csearch,	0,			FALSE},
-    {'<',	nv_operator,	NV_RL,			0},
-    {'=',	nv_operator,	0,			0},
-    {'>',	nv_operator,	NV_RL,			0},
-    {'?',	nv_search,	0,			FALSE},
-    {'@',	nv_at,		NV_NCH_NOP,		FALSE},
-    {'A',	nv_edit,	0,			0},
-    {'B',	nv_bck_word,	0,			1},
-    {'C',	nv_abbrev,	NV_KEEPREG,		0},
-    {'D',	nv_abbrev,	NV_KEEPREG,		0},
-    {'E',	nv_wordcmd,	0,			TRUE},
-    {'F',	nv_csearch,	NV_NCH_ALW|NV_LANG,	BACKWARD},
-    {'G',	nv_goto,	0,			TRUE},
-    {'H',	nv_scroll,	0,			0},
-    {'I',	nv_edit,	0,			0},
-    {'J',	nv_join,	0,			0},
-    {'K',	nv_ident,	0,			0},
-    {'L',	nv_scroll,	0,			0},
-    {'M',	nv_scroll,	0,			0},
-    {'N',	nv_next,	0,			SEARCH_REV},
-    {'O',	nv_open,	0,			0},
-    {'P',	nv_put,		0,			0},
-    {'Q',	nv_exmode,	NV_NCW,			0},
-    {'R',	nv_Replace,	0,			FALSE},
-    {'S',	nv_subst,	NV_KEEPREG,		0},
-    {'T',	nv_csearch,	NV_NCH_ALW|NV_LANG,	BACKWARD},
-    {'U',	nv_Undo,	0,			0},
-    {'V',	nv_visual,	0,			FALSE},
-    {'W',	nv_wordcmd,	0,			TRUE},
-    {'X',	nv_abbrev,	NV_KEEPREG,		0},
-    {'Y',	nv_abbrev,	NV_KEEPREG,		0},
-    {'Z',	nv_Zet,		NV_NCH_NOP|NV_NCW,	0},
-    {'[',	nv_brackets,	NV_NCH_ALW,		BACKWARD},
-    {'\\',	nv_error,	0,			0},
-    {']',	nv_brackets,	NV_NCH_ALW,		FORWARD},
-    {'^',	nv_beginline,	0,			BL_WHITE | BL_FIX},
-    {'_',	nv_lineop,	0,			0},
-    {'`',	nv_gomark,	NV_NCH_ALW,		FALSE},
-    {'a',	nv_edit,	NV_NCH,			0},
-    {'b',	nv_bck_word,	0,			0},
-    {'c',	nv_operator,	0,			0},
-    {'d',	nv_operator,	0,			0},
-    {'e',	nv_wordcmd,	0,			FALSE},
-    {'f',	nv_csearch,	NV_NCH_ALW|NV_LANG,	FORWARD},
-    {'g',	nv_g_cmd,	NV_NCH_ALW,		FALSE},
-    {'h',	nv_left,	NV_RL,			0},
-    {'i',	nv_edit,	NV_NCH,			0},
-    {'j',	nv_down,	0,			FALSE},
-    {'k',	nv_up,		0,			FALSE},
-    {'l',	nv_right,	NV_RL,			0},
-    {'m',	nv_mark,	NV_NCH_NOP,		0},
-    {'n',	nv_next,	0,			0},
-    {'o',	nv_open,	0,			0},
-    {'p',	nv_put,		0,			0},
-    {'q',	nv_record,	NV_NCH,			0},
-    {'r',	nv_replace,	NV_NCH_NOP|NV_LANG,	0},
-    {'s',	nv_subst,	NV_KEEPREG,		0},
-    {'t',	nv_csearch,	NV_NCH_ALW|NV_LANG,	FORWARD},
-    {'u',	nv_undo,	0,			0},
-    {'v',	nv_visual,	0,			FALSE},
-    {'w',	nv_wordcmd,	0,			FALSE},
-    {'x',	nv_abbrev,	NV_KEEPREG,		0},
-    {'y',	nv_operator,	0,			0},
-    {'z',	nv_zet,		NV_NCH_ALW,		0},
-    {'{',	nv_findpar,	0,			BACKWARD},
-    {'|',	nv_pipe,	0,			0},
-    {'}',	nv_findpar,	0,			FORWARD},
-    {'~',	nv_tilde,	0,			0},
-
-    // pound sign
-    {POUND,	nv_ident,	0,			0},
-    {K_MOUSEUP, nv_mousescroll,	0,			MSCR_UP},
-    {K_MOUSEDOWN, nv_mousescroll, 0,			MSCR_DOWN},
-    {K_MOUSELEFT, nv_mousescroll, 0,			MSCR_LEFT},
-    {K_MOUSERIGHT, nv_mousescroll, 0,			MSCR_RIGHT},
-    {K_LEFTMOUSE, nv_mouse,	0,			0},
-    {K_LEFTMOUSE_NM, nv_mouse,	0,			0},
-    {K_LEFTDRAG, nv_mouse,	0,			0},
-    {K_LEFTRELEASE, nv_mouse,	0,			0},
-    {K_LEFTRELEASE_NM, nv_mouse, 0,			0},
-    {K_MOUSEMOVE, nv_mouse,	0,			0},
-    {K_MIDDLEMOUSE, nv_mouse,	0,			0},
-    {K_MIDDLEDRAG, nv_mouse,	0,			0},
-    {K_MIDDLERELEASE, nv_mouse,	0,			0},
-    {K_RIGHTMOUSE, nv_mouse,	0,			0},
-    {K_RIGHTDRAG, nv_mouse,	0,			0},
-    {K_RIGHTRELEASE, nv_mouse,	0,			0},
-    {K_X1MOUSE, nv_mouse,	0,			0},
-    {K_X1DRAG, nv_mouse,	0,			0},
-    {K_X1RELEASE, nv_mouse,	0,			0},
-    {K_X2MOUSE, nv_mouse,	0,			0},
-    {K_X2DRAG, nv_mouse,	0,			0},
-    {K_X2RELEASE, nv_mouse,	0,			0},
-    {K_IGNORE,	nv_ignore,	NV_KEEPREG,		0},
-    {K_NOP,	nv_nop,		0,			0},
-    {K_INS,	nv_edit,	0,			0},
-    {K_KINS,	nv_edit,	0,			0},
-    {K_BS,	nv_ctrlh,	0,			0},
-    {K_UP,	nv_up,		NV_SSS|NV_STS,		FALSE},
-    {K_S_UP,	nv_page,	NV_SS,			BACKWARD},
-    {K_DOWN,	nv_down,	NV_SSS|NV_STS,		FALSE},
-    {K_S_DOWN,	nv_page,	NV_SS,			FORWARD},
-    {K_LEFT,	nv_left,	NV_SSS|NV_STS|NV_RL,	0},
-    {K_S_LEFT,	nv_bck_word,	NV_SS|NV_RL,		0},
-    {K_C_LEFT,	nv_bck_word,	NV_SSS|NV_RL|NV_STS,	1},
-    {K_RIGHT,	nv_right,	NV_SSS|NV_STS|NV_RL,	0},
-    {K_S_RIGHT,	nv_wordcmd,	NV_SS|NV_RL,		FALSE},
-    {K_C_RIGHT,	nv_wordcmd,	NV_SSS|NV_RL|NV_STS,	TRUE},
-    {K_PAGEUP,	nv_page,	NV_SSS|NV_STS,		BACKWARD},
-    {K_KPAGEUP,	nv_page,	NV_SSS|NV_STS,		BACKWARD},
-    {K_PAGEDOWN, nv_page,	NV_SSS|NV_STS,		FORWARD},
-    {K_KPAGEDOWN, nv_page,	NV_SSS|NV_STS,		FORWARD},
-    {K_END,	nv_end,		NV_SSS|NV_STS,		FALSE},
-    {K_KEND,	nv_end,		NV_SSS|NV_STS,		FALSE},
-    {K_S_END,	nv_end,		NV_SS,			FALSE},
-    {K_C_END,	nv_end,		NV_SSS|NV_STS,		TRUE},
-    {K_HOME,	nv_home,	NV_SSS|NV_STS,		0},
-    {K_KHOME,	nv_home,	NV_SSS|NV_STS,		0},
-    {K_S_HOME,	nv_home,	NV_SS,			0},
-    {K_C_HOME,	nv_goto,	NV_SSS|NV_STS,		FALSE},
-    {K_DEL,	nv_abbrev,	0,			0},
-    {K_KDEL,	nv_abbrev,	0,			0},
-    {K_UNDO,	nv_kundo,	0,			0},
-    {K_HELP,	nv_help,	NV_NCW,			0},
-    {K_F1,	nv_help,	NV_NCW,			0},
-    {K_XF1,	nv_help,	NV_NCW,			0},
-    {K_SELECT,	nv_select,	0,			0},
-    {K_VER_SCROLLBAR, NV_VER_SCROLLBAR, 0,		0},
-    {K_HOR_SCROLLBAR, NV_HOR_SCROLLBAR, 0,		0},
-    {K_TABLINE, NV_TABLINE,	0,			0},
-    {K_TABMENU, NV_TABMENU,	0,			0},
-    {K_F21,	NV_NBCMD,	NV_NCH_ALW,		0},
-    {K_DROP,	NV_DROP,	NV_STS,			0},
-    {K_CURSORHOLD, nv_cursorhold, NV_KEEPREG,		0},
-    {K_PS,	nv_edit,	0,			0},
-    {K_COMMAND,	nv_colon,	0,			0},
-    {K_SCRIPT_COMMAND, nv_colon, 0,			0},
-};
-
-// Number of commands in nv_cmds[].
-#define NV_CMDS_SIZE ARRAY_LENGTH(nv_cmds)
+// Declare nv_cmds[].
+#define DO_DECLARE_NVCMD
+#include "nv_cmds.h"
 
 // Include the lookuptable generated by create_nvcmdidx.vim.
 #include "nv_cmdidxs.h"
 
-#if defined(FEAT_EVAL) || defined(PROTO)
-/*
- * Return the command character for the given command index. This function is
- * used to auto-generate nv_cmd_idx[].
- */
-    void
-f_internal_get_nv_cmdchar(typval_T *argvars, typval_T *rettv)
-{
-    int	idx;
-    int	cmd_char;
-
-    rettv->v_type = VAR_NUMBER;
-    rettv->vval.v_number = -1;
-
-    if (check_for_number_arg(argvars, 0) == FAIL)
-	return;
-
-    idx = tv_get_number(&argvars[0]);
-    if (idx < 0 || idx >= (int)NV_CMDS_SIZE)
-	return;
-
-    cmd_char = nv_cmds[idx].cmd_char;
-
-    // We use the absolute value of the character.  Special keys have a
-    // negative value, but are sorted on their absolute value.
-    if (cmd_char < 0)
-	cmd_char = -cmd_char;
-
-    rettv->vval.v_number = cmd_char;
-
-    return;
-}
-#endif
-
 /*
  * Search for a command in the commands table.
  * Returns -1 for invalid command.
new file mode 100644
--- /dev/null
+++ b/src/nv_cmds.h
@@ -0,0 +1,310 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar et al.
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+/*
+ * This file defines the Normal mode commands.
+ */
+
+/*
+ * When adding a Normal/Visual mode command:
+ * 1. Add an entry in the table `nv_cmds[]` below.
+ * 2. Run "make nvcmdidxs" to re-generate nv_cmdidxs.h.
+ * 3. Add an entry in the index for Normal/Visual commands at
+ *    ":help normal-index" and ":help visual-index" .
+ * 4. Add documentation in ../doc/xxx.txt.  Add a tag for both the short and
+ *    long name of the command.
+ */
+
+#ifdef DO_DECLARE_NVCMD
+
+/*
+ * Used when building Vim.
+ */
+# define NVCMD(a, b, c, d)  {a, b, c, d}
+
+#ifdef FEAT_GUI
+#define NV_VER_SCROLLBAR	nv_ver_scrollbar
+#define NV_HOR_SCROLLBAR	nv_hor_scrollbar
+#else
+#define NV_VER_SCROLLBAR nv_error
+#define NV_HOR_SCROLLBAR nv_error
+#endif
+
+#ifdef FEAT_GUI_TABLINE
+#define NV_TABLINE	nv_tabline
+#define NV_TABMENU	nv_tabmenu
+#else
+#define NV_TABLINE	nv_error
+#define NV_TABMENU	nv_error
+#endif
+
+#ifdef FEAT_NETBEANS_INTG
+#define NV_NBCMD	nv_nbcmd
+#else
+#define NV_NBCMD	nv_error
+#endif
+
+#ifdef FEAT_DND
+#define NV_DROP		nv_drop
+#else
+#define NV_DROP		nv_error
+#endif
+
+/*
+ * Function to be called for a Normal or Visual mode command.
+ * The argument is a cmdarg_T.
+ */
+typedef void (*nv_func_T)(cmdarg_T *cap);
+
+// Values for cmd_flags.
+#define NV_NCH	    0x01	  // may need to get a second char
+#define NV_NCH_NOP  (0x02|NV_NCH) // get second char when no operator pending
+#define NV_NCH_ALW  (0x04|NV_NCH) // always get a second char
+#define NV_LANG	    0x08	// second char needs language adjustment
+
+#define NV_SS	    0x10	// may start selection
+#define NV_SSS	    0x20	// may start selection with shift modifier
+#define NV_STS	    0x40	// may stop selection without shift modif.
+#define NV_RL	    0x80	// 'rightleft' modifies command
+#define NV_KEEPREG  0x100	// don't clear regname
+#define NV_NCW	    0x200	// not allowed in command-line window
+
+/*
+ * Generally speaking, every Normal mode command should either clear any
+ * pending operator (with *clearop*()), or set the motion type variable
+ * oap->motion_type.
+ *
+ * When a cursor motion command is made, it is marked as being a character or
+ * line oriented motion.  Then, if an operator is in effect, the operation
+ * becomes character or line oriented accordingly.
+ */
+
+/*
+ * This table contains one entry for every Normal or Visual mode command.
+ * The order doesn't matter, this will be sorted by the create_nvcmdidx.vim
+ * script to generate the nv_cmd_idx[] lookup table.
+ * It is faster when all keys from zero to '~' are present.
+ */
+static const struct nv_cmd
+{
+    int		cmd_char;	// (first) command character
+    nv_func_T   cmd_func;	// function for this command
+    short_u	cmd_flags;	// NV_ flags
+    short	cmd_arg;	// value for ca.arg
+} nv_cmds[] =
+
+#else  // DO_DECLARE_NVCMD
+
+/*
+ * Used when creating nv_cmdidxs.h.
+ */
+# define NVCMD(a, b, c, d)  a
+static const int nv_cmds[] =
+
+#endif // DO_DECLARE_NVCMD
+{
+    NVCMD(NUL,		nv_error,	0,			0),
+    NVCMD(Ctrl_A,	nv_addsub,	0,			0),
+    NVCMD(Ctrl_B,	nv_page,	NV_STS,			BACKWARD),
+    NVCMD(Ctrl_C,	nv_esc,		0,			TRUE),
+    NVCMD(Ctrl_D,	nv_halfpage,	0,			0),
+    NVCMD(Ctrl_E,	nv_scroll_line,	0,			TRUE),
+    NVCMD(Ctrl_F,	nv_page,	NV_STS,			FORWARD),
+    NVCMD(Ctrl_G,	nv_ctrlg,	0,			0),
+    NVCMD(Ctrl_H,	nv_ctrlh,	0,			0),
+    NVCMD(Ctrl_I,	nv_pcmark,	0,			0),
+    NVCMD(NL,		nv_down,	0,			FALSE),
+    NVCMD(Ctrl_K,	nv_error,	0,			0),
+    NVCMD(Ctrl_L,	nv_clear,	0,			0),
+    NVCMD(CAR,		nv_down,	0,			TRUE),
+    NVCMD(Ctrl_N,	nv_down,	NV_STS,			FALSE),
+    NVCMD(Ctrl_O,	nv_ctrlo,	0,			0),
+    NVCMD(Ctrl_P,	nv_up,		NV_STS,			FALSE),
+    NVCMD(Ctrl_Q,	nv_visual,	0,			FALSE),
+    NVCMD(Ctrl_R,	nv_redo_or_register, 0,			0),
+    NVCMD(Ctrl_S,	nv_ignore,	0,			0),
+    NVCMD(Ctrl_T,	nv_tagpop,	NV_NCW,			0),
+    NVCMD(Ctrl_U,	nv_halfpage,	0,			0),
+    NVCMD(Ctrl_V,	nv_visual,	0,			FALSE),
+    NVCMD(Ctrl_W,	nv_window,	0,			0),
+    NVCMD(Ctrl_X,	nv_addsub,	0,			0),
+    NVCMD(Ctrl_Y,	nv_scroll_line,	0,			FALSE),
+    NVCMD(Ctrl_Z,	nv_suspend,	0,			0),
+    NVCMD(ESC,		nv_esc,		0,			FALSE),
+    NVCMD(Ctrl_BSL,	nv_normal,	NV_NCH_ALW,		0),
+    NVCMD(Ctrl_RSB,	nv_ident,	NV_NCW,			0),
+    NVCMD(Ctrl_HAT,	nv_hat,		NV_NCW,			0),
+    NVCMD(Ctrl__,	nv_error,	0,			0),
+    NVCMD(' ',		nv_right,	0,			0),
+    NVCMD('!',		nv_operator,	0,			0),
+    NVCMD('"',		nv_regname,	NV_NCH_NOP|NV_KEEPREG,	0),
+    NVCMD('#',		nv_ident,	0,			0),
+    NVCMD('$',		nv_dollar,	0,			0),
+    NVCMD('%',		nv_percent,	0,			0),
+    NVCMD('&',		nv_optrans,	0,			0),
+    NVCMD('\'',		nv_gomark,	NV_NCH_ALW,		TRUE),
+    NVCMD('(',		nv_brace,	0,			BACKWARD),
+    NVCMD(')',		nv_brace,	0,			FORWARD),
+    NVCMD('*',		nv_ident,	0,			0),
+    NVCMD('+',		nv_down,	0,			TRUE),
+    NVCMD(',',		nv_csearch,	0,			TRUE),
+    NVCMD('-',		nv_up,		0,			TRUE),
+    NVCMD('.',		nv_dot,		NV_KEEPREG,		0),
+    NVCMD('/',		nv_search,	0,			FALSE),
+    NVCMD('0',		nv_beginline,	0,			0),
+    NVCMD('1',		nv_ignore,	0,			0),
+    NVCMD('2',		nv_ignore,	0,			0),
+    NVCMD('3',		nv_ignore,	0,			0),
+    NVCMD('4',		nv_ignore,	0,			0),
+    NVCMD('5',		nv_ignore,	0,			0),
+    NVCMD('6',		nv_ignore,	0,			0),
+    NVCMD('7',		nv_ignore,	0,			0),
+    NVCMD('8',		nv_ignore,	0,			0),
+    NVCMD('9',		nv_ignore,	0,			0),
+    NVCMD(':',		nv_colon,	0,			0),
+    NVCMD(';',		nv_csearch,	0,			FALSE),
+    NVCMD('<',		nv_operator,	NV_RL,			0),
+    NVCMD('=',		nv_operator,	0,			0),
+    NVCMD('>',		nv_operator,	NV_RL,			0),
+    NVCMD('?',		nv_search,	0,			FALSE),
+    NVCMD('@',		nv_at,		NV_NCH_NOP,		FALSE),
+    NVCMD('A',		nv_edit,	0,			0),
+    NVCMD('B',		nv_bck_word,	0,			1),
+    NVCMD('C',		nv_abbrev,	NV_KEEPREG,		0),
+    NVCMD('D',		nv_abbrev,	NV_KEEPREG,		0),
+    NVCMD('E',		nv_wordcmd,	0,			TRUE),
+    NVCMD('F',		nv_csearch,	NV_NCH_ALW|NV_LANG,	BACKWARD),
+    NVCMD('G',		nv_goto,	0,			TRUE),
+    NVCMD('H',		nv_scroll,	0,			0),
+    NVCMD('I',		nv_edit,	0,			0),
+    NVCMD('J',		nv_join,	0,			0),
+    NVCMD('K',		nv_ident,	0,			0),
+    NVCMD('L',		nv_scroll,	0,			0),
+    NVCMD('M',		nv_scroll,	0,			0),
+    NVCMD('N',		nv_next,	0,			SEARCH_REV),
+    NVCMD('O',		nv_open,	0,			0),
+    NVCMD('P',		nv_put,		0,			0),
+    NVCMD('Q',		nv_exmode,	NV_NCW,			0),
+    NVCMD('R',		nv_Replace,	0,			FALSE),
+    NVCMD('S',		nv_subst,	NV_KEEPREG,		0),
+    NVCMD('T',		nv_csearch,	NV_NCH_ALW|NV_LANG,	BACKWARD),
+    NVCMD('U',		nv_Undo,	0,			0),
+    NVCMD('V',		nv_visual,	0,			FALSE),
+    NVCMD('W',		nv_wordcmd,	0,			TRUE),
+    NVCMD('X',		nv_abbrev,	NV_KEEPREG,		0),
+    NVCMD('Y',		nv_abbrev,	NV_KEEPREG,		0),
+    NVCMD('Z',		nv_Zet,		NV_NCH_NOP|NV_NCW,	0),
+    NVCMD('[',		nv_brackets,	NV_NCH_ALW,		BACKWARD),
+    NVCMD('\\',		nv_error,	0,			0),
+    NVCMD(']',		nv_brackets,	NV_NCH_ALW,		FORWARD),
+    NVCMD('^',		nv_beginline,	0,		    BL_WHITE | BL_FIX),
+    NVCMD('_',		nv_lineop,	0,			0),
+    NVCMD('`',		nv_gomark,	NV_NCH_ALW,		FALSE),
+    NVCMD('a',		nv_edit,	NV_NCH,			0),
+    NVCMD('b',		nv_bck_word,	0,			0),
+    NVCMD('c',		nv_operator,	0,			0),
+    NVCMD('d',		nv_operator,	0,			0),
+    NVCMD('e',		nv_wordcmd,	0,			FALSE),
+    NVCMD('f',		nv_csearch,	NV_NCH_ALW|NV_LANG,	FORWARD),
+    NVCMD('g',		nv_g_cmd,	NV_NCH_ALW,		FALSE),
+    NVCMD('h',		nv_left,	NV_RL,			0),
+    NVCMD('i',		nv_edit,	NV_NCH,			0),
+    NVCMD('j',		nv_down,	0,			FALSE),
+    NVCMD('k',		nv_up,		0,			FALSE),
+    NVCMD('l',		nv_right,	NV_RL,			0),
+    NVCMD('m',		nv_mark,	NV_NCH_NOP,		0),
+    NVCMD('n',		nv_next,	0,			0),
+    NVCMD('o',		nv_open,	0,			0),
+    NVCMD('p',		nv_put,		0,			0),
+    NVCMD('q',		nv_record,	NV_NCH,			0),
+    NVCMD('r',		nv_replace,	NV_NCH_NOP|NV_LANG,	0),
+    NVCMD('s',		nv_subst,	NV_KEEPREG,		0),
+    NVCMD('t',		nv_csearch,	NV_NCH_ALW|NV_LANG,	FORWARD),
+    NVCMD('u',		nv_undo,	0,			0),
+    NVCMD('v',		nv_visual,	0,			FALSE),
+    NVCMD('w',		nv_wordcmd,	0,			FALSE),
+    NVCMD('x',		nv_abbrev,	NV_KEEPREG,		0),
+    NVCMD('y',		nv_operator,	0,			0),
+    NVCMD('z',		nv_zet,		NV_NCH_ALW,		0),
+    NVCMD('{',		nv_findpar,	0,			BACKWARD),
+    NVCMD('|',		nv_pipe,	0,			0),
+    NVCMD('}',		nv_findpar,	0,			FORWARD),
+    NVCMD('~',		nv_tilde,	0,			0),
+
+    // pound sign
+    NVCMD(POUND,	nv_ident,	0,			0),
+    NVCMD(K_MOUSEUP,	nv_mousescroll,	0,			MSCR_UP),
+    NVCMD(K_MOUSEDOWN,	nv_mousescroll, 0,			MSCR_DOWN),
+    NVCMD(K_MOUSELEFT,	nv_mousescroll, 0,			MSCR_LEFT),
+    NVCMD(K_MOUSERIGHT, nv_mousescroll, 0,			MSCR_RIGHT),
+    NVCMD(K_LEFTMOUSE,	nv_mouse,	0,			0),
+    NVCMD(K_LEFTMOUSE_NM, nv_mouse,	0,			0),
+    NVCMD(K_LEFTDRAG,	nv_mouse,	0,			0),
+    NVCMD(K_LEFTRELEASE, nv_mouse,	0,			0),
+    NVCMD(K_LEFTRELEASE_NM, nv_mouse,	0,			0),
+    NVCMD(K_MOUSEMOVE,	nv_mouse,	0,			0),
+    NVCMD(K_MIDDLEMOUSE, nv_mouse,	0,			0),
+    NVCMD(K_MIDDLEDRAG, nv_mouse,	0,			0),
+    NVCMD(K_MIDDLERELEASE, nv_mouse,	0,			0),
+    NVCMD(K_RIGHTMOUSE, nv_mouse,	0,			0),
+    NVCMD(K_RIGHTDRAG,	nv_mouse,	0,			0),
+    NVCMD(K_RIGHTRELEASE, nv_mouse,	0,			0),
+    NVCMD(K_X1MOUSE,	nv_mouse,	0,			0),
+    NVCMD(K_X1DRAG,	nv_mouse,	0,			0),
+    NVCMD(K_X1RELEASE,	nv_mouse,	0,			0),
+    NVCMD(K_X2MOUSE,	nv_mouse,	0,			0),
+    NVCMD(K_X2DRAG,	nv_mouse,	0,			0),
+    NVCMD(K_X2RELEASE,	nv_mouse,	0,			0),
+    NVCMD(K_IGNORE,	nv_ignore,	NV_KEEPREG,		0),
+    NVCMD(K_NOP,	nv_nop,		0,			0),
+    NVCMD(K_INS,	nv_edit,	0,			0),
+    NVCMD(K_KINS,	nv_edit,	0,			0),
+    NVCMD(K_BS,		nv_ctrlh,	0,			0),
+    NVCMD(K_UP,		nv_up,		NV_SSS|NV_STS,		FALSE),
+    NVCMD(K_S_UP,	nv_page,	NV_SS,			BACKWARD),
+    NVCMD(K_DOWN,	nv_down,	NV_SSS|NV_STS,		FALSE),
+    NVCMD(K_S_DOWN,	nv_page,	NV_SS,			FORWARD),
+    NVCMD(K_LEFT,	nv_left,	NV_SSS|NV_STS|NV_RL,	0),
+    NVCMD(K_S_LEFT,	nv_bck_word,	NV_SS|NV_RL,		0),
+    NVCMD(K_C_LEFT,	nv_bck_word,	NV_SSS|NV_RL|NV_STS,	1),
+    NVCMD(K_RIGHT,	nv_right,	NV_SSS|NV_STS|NV_RL,	0),
+    NVCMD(K_S_RIGHT,	nv_wordcmd,	NV_SS|NV_RL,		FALSE),
+    NVCMD(K_C_RIGHT,	nv_wordcmd,	NV_SSS|NV_RL|NV_STS,	TRUE),
+    NVCMD(K_PAGEUP,	nv_page,	NV_SSS|NV_STS,		BACKWARD),
+    NVCMD(K_KPAGEUP,	nv_page,	NV_SSS|NV_STS,		BACKWARD),
+    NVCMD(K_PAGEDOWN,	nv_page,	NV_SSS|NV_STS,		FORWARD),
+    NVCMD(K_KPAGEDOWN,	nv_page,	NV_SSS|NV_STS,		FORWARD),
+    NVCMD(K_END,	nv_end,		NV_SSS|NV_STS,		FALSE),
+    NVCMD(K_KEND,	nv_end,		NV_SSS|NV_STS,		FALSE),
+    NVCMD(K_S_END,	nv_end,		NV_SS,			FALSE),
+    NVCMD(K_C_END,	nv_end,		NV_SSS|NV_STS,		TRUE),
+    NVCMD(K_HOME,	nv_home,	NV_SSS|NV_STS,		0),
+    NVCMD(K_KHOME,	nv_home,	NV_SSS|NV_STS,		0),
+    NVCMD(K_S_HOME,	nv_home,	NV_SS,			0),
+    NVCMD(K_C_HOME,	nv_goto,	NV_SSS|NV_STS,		FALSE),
+    NVCMD(K_DEL,	nv_abbrev,	0,			0),
+    NVCMD(K_KDEL,	nv_abbrev,	0,			0),
+    NVCMD(K_UNDO,	nv_kundo,	0,			0),
+    NVCMD(K_HELP,	nv_help,	NV_NCW,			0),
+    NVCMD(K_F1,		nv_help,	NV_NCW,			0),
+    NVCMD(K_XF1,	nv_help,	NV_NCW,			0),
+    NVCMD(K_SELECT,	nv_select,	0,			0),
+    NVCMD(K_VER_SCROLLBAR, NV_VER_SCROLLBAR, 0,			0),
+    NVCMD(K_HOR_SCROLLBAR, NV_HOR_SCROLLBAR, 0,			0),
+    NVCMD(K_TABLINE,	NV_TABLINE,	0,			0),
+    NVCMD(K_TABMENU,	NV_TABMENU,	0,			0),
+    NVCMD(K_F21,	NV_NBCMD,	NV_NCH_ALW,		0),
+    NVCMD(K_DROP,	NV_DROP,	NV_STS,			0),
+    NVCMD(K_CURSORHOLD, nv_cursorhold,	NV_KEEPREG,		0),
+    NVCMD(K_PS,		nv_edit,	0,			0),
+    NVCMD(K_COMMAND,	nv_colon,	0,			0),
+    NVCMD(K_SCRIPT_COMMAND, nv_colon,	0,			0),
+};
+
+// Number of commands in nv_cmds[].
+#define NV_CMDS_SIZE ARRAY_LENGTH(nv_cmds)
--- a/src/proto/normal.pro
+++ b/src/proto/normal.pro
@@ -1,5 +1,4 @@
 /* normal.c */
-void f_internal_get_nv_cmdchar(typval_T *argvars, typval_T *rettv);
 void normal_cmd(oparg_T *oap, int toplevel);
 void check_visual_highlight(void);
 void end_visual_mode(void);
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4270,
+/**/
     4269,
 /**/
     4268,