aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/CMakeLists.txt5
-rw-r--r--config/config.h.in5
-rw-r--r--runtime/doc/change.txt4
-rw-r--r--runtime/doc/debugger.txt107
-rw-r--r--runtime/doc/editing.txt2
-rw-r--r--runtime/doc/eval.txt27
-rw-r--r--runtime/doc/gui.txt2
-rw-r--r--runtime/doc/options.txt35
-rw-r--r--runtime/doc/tabpage.txt4
-rw-r--r--runtime/doc/various.txt5
-rw-r--r--runtime/doc/vi_diff.txt3
-rw-r--r--src/nvim/edit.c83
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/fileio.c48
-rw-r--r--src/nvim/keymap.c32
-rw-r--r--src/nvim/normal.c6
-rw-r--r--src/nvim/os/fs.c4
-rw-r--r--src/nvim/os/os_defs.h19
-rw-r--r--src/nvim/os/shell.c25
-rw-r--r--src/nvim/os/unix_defs.h12
-rw-r--r--src/nvim/os/win_defs.h5
-rw-r--r--src/nvim/os_unix.c4
-rw-r--r--src/nvim/strings.c48
-rw-r--r--src/nvim/terminal.c26
-rw-r--r--test/functional/terminal/tui_spec.lua21
-rw-r--r--test/unit/os/shell_spec.lua60
-rw-r--r--test/unit/strings_spec.lua69
27 files changed, 346 insertions, 317 deletions
diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt
index c64e7e1ddb..48ecc9cc33 100644
--- a/config/CMakeLists.txt
+++ b/config/CMakeLists.txt
@@ -8,7 +8,6 @@ include(CheckCSourceCompiles)
check_type_size("int" SIZEOF_INT)
check_type_size("long" SIZEOF_LONG)
check_type_size("intmax_t" SIZEOF_INTMAX_T)
-check_type_size("off_t" SIZEOF_OFF_T)
check_type_size("size_t" SIZEOF_SIZE_T)
check_type_size("long long" SIZEOF_LONG_LONG)
check_type_size("void *" SIZEOF_VOID_PTR)
@@ -16,16 +15,12 @@ check_type_size("void *" SIZEOF_VOID_PTR)
check_symbol_exists(_NSGetEnviron crt_externs.h HAVE__NSGETENVIRON)
# Headers
-check_include_files(dirent.h HAVE_DIRENT_H)
check_include_files(fcntl.h HAVE_FCNTL_H)
check_include_files(iconv.h HAVE_ICONV_H)
check_include_files(langinfo.h HAVE_LANGINFO_H)
-check_include_files(libgen.h HAVE_LIBGEN_H)
check_include_files(locale.h HAVE_LOCALE_H)
check_include_files(pwd.h HAVE_PWD_H)
check_include_files(strings.h HAVE_STRINGS_H)
-check_include_files(stropts.h HAVE_STROPTS_H)
-check_include_files(sys/param.h HAVE_SYS_PARAM_H)
check_include_files(sys/wait.h HAVE_SYS_WAIT_H)
if(NOT HAVE_SYS_WAIT_H AND UNIX)
# See if_cscope.c
diff --git a/config/config.h.in b/config/config.h.in
index b442a732e5..84a90301ff 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -5,7 +5,6 @@
#define SIZEOF_INT @SIZEOF_INT@
#define SIZEOF_LONG @SIZEOF_LONG@
-#define SIZEOF_OFF_T @SIZEOF_OFF_T@
#if @SIZEOF_VOID_PTR@ == 8
#define ARCH_64
@@ -14,7 +13,6 @@
#endif
#cmakedefine HAVE__NSGETENVIRON
-#cmakedefine HAVE_CRT_EXTERNS_H
#cmakedefine HAVE_FCNTL_H
#cmakedefine HAVE_FD_CLOEXEC
#cmakedefine HAVE_FSEEKO
@@ -24,7 +22,6 @@
#cmakedefine HAVE_ICONV
#cmakedefine HAVE_ICONV_H
#cmakedefine HAVE_LANGINFO_H
-#cmakedefine HAVE_LIBGEN_H
#cmakedefine HAVE_LOCALE_H
#cmakedefine HAVE_NL_LANGINFO_CODESET
#cmakedefine HAVE_NL_MSG_CAT_CNTR
@@ -41,8 +38,6 @@
#cmakedefine HAVE_STRCASECMP
#cmakedefine HAVE_STRINGS_H
#cmakedefine HAVE_STRNCASECMP
-#cmakedefine HAVE_STROPTS_H
-#cmakedefine HAVE_SYS_PARAM_H
#cmakedefine HAVE_SYS_UTSNAME_H
#cmakedefine HAVE_SYS_WAIT_H
#cmakedefine HAVE_UNISTD_H
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index b461aa521f..f01def6ab2 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -832,13 +832,13 @@ This replaces each 'E' character with a euro sign. Read more in |<Char->|.
:promptf[ind] [string]
Put up a Search dialog. When [string] is given, it is
used as the initial search string.
- {only for Win32, Motif and GTK GUI}
+ {only for Win32 and GTK GUI}
*:promptr* *:promptrepl*
:promptr[epl] [string]
Put up a Search/Replace dialog. When [string] is
given, it is used as the initial search string.
- {only for Win32, Motif and GTK GUI}
+ {only for Win32 and GTK GUI}
4.4 Changing tabs *change-tabs*
diff --git a/runtime/doc/debugger.txt b/runtime/doc/debugger.txt
deleted file mode 100644
index f1eb5639bd..0000000000
--- a/runtime/doc/debugger.txt
+++ /dev/null
@@ -1,107 +0,0 @@
-*debugger.txt* For Vim version 7.4. Last change: 2005 Mar 29
-
-
- VIM REFERENCE MANUAL by Gordon Prieur
-
-
-Debugger Support Features *debugger-support*
-
-1. Debugger Features |debugger-features|
-
-==============================================================================
-1. Debugger Features *debugger-features*
-
-The following features are available for an integration with a debugger or
-an Integrated Programming Environment (IPE) or Integrated Development
-Environment (IDE):
-
- Alternate Command Input |alt-input|
- Debug Signs |debug-signs|
- Debug Source Highlight |debug-highlight|
- Message Footer |gui-footer|
- Balloon Evaluation |balloon-eval|
-
-These features were added specifically for use in the Motif version of gvim.
-However, the |alt-input| and |debug-highlight| were written to be usable in
-both vim and gvim. Some of the other features could be used in the non-GUI
-vim with slight modifications. However, I did not do this nor did I test the
-reliability of building for vim or non Motif GUI versions.
-
-
-1.1 Alternate Command Input *alt-input*
-
-For Vim to work with a debugger there must be at least an input connection
-with a debugger or external tool. In many cases there will also be an output
-connection but this isn't absolutely necessary.
-
-The purpose of the input connection is to let the external debugger send
-commands to Vim. The commands sent by the debugger should give the debugger
-enough control to display the current debug environment and state.
-
-The current implementation is based on the X Toolkit dispatch loop and the
-XtAddInput() function call.
-
-
-1.2 Debug Signs *debug-signs*
-
-Many debuggers mark specific lines by placing a small sign or color highlight
-on the line. The |:sign| command lets the debugger set this graphic mark. Some
-examples where this feature would be used would be a debugger showing an arrow
-representing the Program Counter (PC) of the program being debugged. Another
-example would be a small stop sign for a line with a breakpoint. These visible
-highlights let the user keep track of certain parts of the state of the
-debugger.
-
-This feature can be used with more than debuggers, too. An IPE can use a sign
-to highlight build errors, searched text, or other things. The sign feature
-can also work together with the |debug-highlight| to ensure the mark is
-highly visible.
-
-Debug signs are defined and placed using the |:sign| command.
-
-
-1.3 Debug Source Highlight *debug-highlight*
-
-This feature allows a line to have a predominant highlight. The highlight is
-intended to make a specific line stand out. The highlight could be made to
-work for both vim and gvim, whereas the debug sign is, in most cases, limited
-to gvim. The one exception to this is Sun Microsystem's dtterm. The dtterm
-from Sun has a "sign gutter" for showing signs.
-
-
-1.4 Message Footer *gui-footer*
-
-The message footer can be used to display messages from a debugger or IPE. It
-can also be used to display menu and toolbar tips. The footer area is at the
-bottom of the GUI window, below the line used to display colon commands.
-
-The display of the footer is controlled by the 'guioptions' letter 'F'.
-
-
-1.5 Balloon Evaluation *balloon-eval*
-
-This feature allows a debugger, or other external tool, to display dynamic
-information based on where the mouse is pointing. The purpose of this feature
-was to allow Sun's Visual WorkShop debugger to display expression evaluations.
-However, the feature was implemented in as general a manner as possible and
-could be used for displaying other information as well.
-
-The Balloon Evaluation has some settable parameters too. For Motif the font
-list and colors can be set via X resources (XmNballoonEvalFontList,
-XmNballoonEvalBackground, and XmNballoonEvalForeground).
-The 'balloondelay' option sets the delay before an attempt is made to show a
-balloon.
-The 'ballooneval' option needs to be set to switch it on.
-
-Balloon evaluation is only available when compiled with the |+balloon_eval|
-feature.
-
-The Balloon evaluation functions are also used to show a tooltip for the
-toolbar. The 'ballooneval' option does not need to be set for this. But the
-other settings apply.
-
-Another way to use the balloon is with the 'balloonexpr' option. This is
-completely user definable.
-
-==============================================================================
- vim:tw=78:sw=4:ts=8:ft=help:norl:
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 4fdc963f9c..1044236826 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1145,7 +1145,7 @@ If you want to always use ":confirm", set the 'confirm' option.
|:diffsplit|, |:diffpatch|, |:pedit|, |:redir|,
|:source|, |:update|, |:visual|, |:vsplit|,
and |:qall| if 'confirm' is set.
- {only in Win32, Motif, GTK and Mac GUI}
+ {only in Win32, GTK and Mac GUI}
When ":browse" is not possible you get an error
message. If the |+browse| feature is missing or the
{command} doesn't support browsing, the {command} is
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 38ac74f0af..5b9c0bf702 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2563,10 +2563,10 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
{default} is omitted, 1 is used.
The optional {type} argument gives the type of dialog. This
- is only used for the icon of the GTK, Mac, Motif and Win32
- GUI. It can be one of these values: "Error", "Question",
- "Info", "Warning" or "Generic". Only the first character is
- relevant. When {type} is omitted, "Generic" is used.
+ is only used for the icon of the GTK, Mac, and Win32 GUI. It
+ can be one of these values: "Error", "Question", "Info",
+ "Warning" or "Generic". Only the first character is relevant.
+ When {type} is omitted, "Generic" is used.
If the user aborts the dialog by pressing <Esc>, CTRL-C,
or another valid interrupt key, confirm() returns 0.
@@ -3279,7 +3279,7 @@ foreground() Move the Vim window to the foreground. Useful when sent from
On Win32 systems this might not work, the OS does not always
allow a window to bring itself to the foreground. Use
|remote_foreground()| instead.
- {only in the Win32, Motif and GTK GUI versions and the
+ {only in the Win32 and GTK GUI versions and the
Win32 console version}
@@ -4180,9 +4180,14 @@ jobsend({job}, {data}) {Nvim} *jobsend()*
jobstart({cmd}[, {opts}]) {Nvim} *jobstart()*
Spawns {cmd} as a job. If {cmd} is a |List|, it will be run
- directly. If {cmd} is a |string|, it will be equivalent to >
- :call jobstart([&shell, &shellcmdflag, '{cmd}'])
-< If passed, {opts} must be a dictionary with any of the
+ directly. If {cmd} is a |string|, it will be roughly
+ equivalent to >
+ :call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
+< NOTE: read |shell-unquoting| before constructing any lists
+ with 'shell' or 'shellcmdflag' options. The above call is
+ only written to show the idea, one needs to perform unquoting
+ and do split taking quotes into account.
+ If passed, {opts} must be a dictionary with any of the
following keys:
- on_stdout: stdout event handler
- on_stderr: stderr event handler
@@ -5208,7 +5213,7 @@ remote_foreground({server}) *remote_foreground()*
Note: This does not restore the window if it was minimized,
like foreground() does.
This function is not available in the |sandbox|.
- {only in the Win32, Motif and GTK GUI versions and the
+ {only in the Win32 and GTK GUI versions and the
Win32 console version}
@@ -6958,8 +6963,6 @@ There are four types of features:
acl Compiled with |ACL| support.
arabic Compiled with Arabic support |Arabic|.
autocmd Compiled with autocommand support. |autocommand|
-balloon_eval Compiled with |balloon-eval| support.
-balloon_multiline GUI supports multiline balloons.
browse Compiled with |:browse| support, and browse() will
work.
browsefilter Compiled with support for |browsefilter|.
@@ -6993,14 +6996,12 @@ float Compiled with support for |Float|.
fname_case Case in file names matters (for Windows this is not
present).
folding Compiled with |folding| support.
-footer Compiled with GUI footer support. |gui-footer|
gettext Compiled with message translation |multi-lang|
gui Compiled with GUI enabled.
gui_gnome Compiled with Gnome support (gui_gtk is also defined).
gui_gtk Compiled with GTK+ GUI (any version).
gui_gtk2 Compiled with GTK+ 2 GUI (gui_gtk is also defined).
gui_mac Compiled with Macintosh GUI.
-gui_motif Compiled with Motif GUI.
gui_running Vim is running in the GUI, or it will start soon.
gui_win32 Compiled with MS Windows Win32 GUI.
iconv Can use iconv() for conversion.
diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt
index 1b0db53d6d..eeeeb3cd20 100644
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -594,7 +594,7 @@ When no or zero priority is given, 500 is used.
The priority for the PopUp menu is not used.
The Help menu will be placed on the far right side of the menu bar on systems
-which support this (Motif and GTK+). For GTK+ 2, this is not done anymore
+which support this (GTK+). For GTK+ 2, this is not done anymore
because right-aligning the Help menu is now discouraged UI design.
You can use a priority higher than 9999, to make it go after the Help menu,
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index c3fb23a3f7..718125d9dc 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -3184,8 +3184,6 @@ A jump table for the options with a short description can be found at |Q_op|.
the right moment, try adding this flag. This must be done
before starting the GUI. Set it in your |gvimrc|. Adding or
removing it after the GUI has started has no effect.
- *'go-F'*
- 'F' Add a footer. Only for Motif. See |gui-footer|.
*'guipty'* *'noguipty'*
@@ -5385,11 +5383,32 @@ A jump table for the options with a short description can be found at |Q_op|.
If the name of the shell contains a space, you might need to enclose
it in quotes. Example: >
:set shell=\"c:\program\ files\unix\sh.exe\"\ -f
-< Note the backslash before each quote (to avoid starting a comment) and
- each space (to avoid ending the option value). Also note that the
- "-f" is not inside the quotes, because it is not part of the command
- name. And Vim automagically recognizes the backslashes that are path
- separators.
+< Note the backslash before each quote (to avoid starting a comment) and
+ each space (to avoid ending the option value), so better use |:let-&|
+ like this: >
+ :let &shell='"C:\Program Files\unix\sh.exe" -f'
+< Also note that the "-f" is not inside the quotes, because it is not
+ part of the command name.
+ *shell-unquoting*
+ Rules regarding quotes:
+ 1. Option is split on space and tab characters that are not inside
+ quotes: "abc def" runs shell named "abc" with additional argument
+ "def", '"abc def"' runs shell named "abc def" with no additional
+ arguments (here and below: additional means “additional to
+ 'shellcmdflag'”).
+ 2. Quotes in option may be present in any position and any number:
+ '"abc"', '"a"bc', 'a"b"c', 'ab"c"' and '"a"b"c"' are all equivalent
+ to just "abc".
+ 3. Inside quotes backslash preceding backslash means one backslash.
+ Backslash preceding quote means one quote. Backslash preceding
+ anything else means backslash and next character literally:
+ '"a\\b"' is the same as "a\b", '"a\\"b"' runs shell named literally
+ 'a"b', '"a\b"' is the same as "a\b" again.
+ 4. Outside of quotes backslash always means itself, it cannot be used
+ to escape quote: 'a\"b"' is the same as "a\b".
+ Note that such processing is done after |:set| did its own round of
+ unescaping, so to keep yourself sane use |:let-&| like shown above.
+
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@@ -5405,6 +5424,8 @@ A jump table for the options with a short description can be found at |Q_op|.
On Unix it can have more than one flag. Each white space separated
part is passed as an argument to the shell command.
See |option-backslash| about including spaces and backslashes.
+ See |shell-unquoting| which talks about separating this option into
+ multiple arguments.
Also see |dos-shell| for MS-DOS and MS-Windows.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt
index 84eceee34f..13944dc02a 100644
--- a/runtime/doc/tabpage.txt
+++ b/runtime/doc/tabpage.txt
@@ -48,8 +48,8 @@ A double click with the mouse in the non-GUI tab pages line opens a new, empty
tab page. It is placed left of the position of the click. The first click
may select another tab page first, causing an extra screen update.
-This also works in a few GUI versions, esp. Win32 and Motif. But only when
-clicking right of the labels.
+This also works in a few GUI versions, esp. Win32. But only when clicking
+right of the labels.
In the GUI tab pages line you can use the right mouse button to open menu.
|tabline-menu|.
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 9e4104bf92..efe20571ac 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -295,9 +295,6 @@ g8 Print the hex values of the bytes used in the
*+acl* |ACL| support included
B *+arabic* |Arabic| language support
N *+autocmd* |:autocmd|, automatic commands
-m *+balloon_eval* |balloon-eval| support. Included when compiling with
- supported GUI (Motif, GTK, GUI) and either
- Netbeans/Sun Workshop integration or |+eval| feature.
N *+browse* |:browse| command
N *+byte_offset* support for 'o' flag in 'statusline' option, "go"
and ":goto" commands.
@@ -327,10 +324,8 @@ N *+file_in_path* |gf|, |CTRL-W_f| and |<cfile>|
N *+find_in_path* include file searches: |[I|, |:isearch|,
|CTRL-W_CTRL-I|, |:checkpath|, etc.
N *+folding* |folding|
- *+footer* |gui-footer|
N *+gettext* message translations |multi-lang|
*+GUI_GTK* Unix only: GTK+ |GUI|
- *+GUI_Motif* Unix only: Motif |GUI|
*+iconv* Compiled with the |iconv()| function
*+iconv/dyn* Likewise |iconv-dynamic| |/dyn|
N *+insert_expand* |insert_expand| Insert mode completion
diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt
index 9d3a2f5118..1553fe93d1 100644
--- a/runtime/doc/vi_diff.txt
+++ b/runtime/doc/vi_diff.txt
@@ -77,8 +77,7 @@ Graphical User Interface (GUI). |gui|
Included support for GUI: menu's, mouse, scrollbars, etc. You can
define your own menus. Better support for CTRL/SHIFT/ALT keys in
combination with special keys and mouse. Supported for various
- platforms, such as X11 (with a Motif interface), GTK, Win32
- (Windows 95 and later), and Macintosh.
+ platforms such as GTK, Win32, and Macintosh.
Multiple windows and buffers. |windows.txt|
Vim can split the screen into several windows, each editing a
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index b3222b0781..ccfc9b4803 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1352,24 +1352,21 @@ ins_redraw (
if (char_avail())
return;
- /* Trigger CursorMoved if the cursor moved. Not when the popup menu is
- * visible, the command might delete it. */
- if (ready && (
- has_cursormovedI()
- ||
- curwin->w_p_cole > 0
- )
+ // Trigger CursorMoved if the cursor moved. Not when the popup menu is
+ // visible, the command might delete it.
+ if (ready && (has_event(EVENT_CURSORMOVEDI) || curwin->w_p_cole > 0)
&& !equalpos(last_cursormoved, curwin->w_cursor)
- && !pum_visible()
- ) {
- /* Need to update the screen first, to make sure syntax
- * highlighting is correct after making a change (e.g., inserting
- * a "(". The autocommand may also require a redraw, so it's done
- * again below, unfortunately. */
- if (syntax_present(curwin) && must_redraw)
+ && !pum_visible()) {
+ // Need to update the screen first, to make sure syntax
+ // highlighting is correct after making a change (e.g., inserting
+ // a "(". The autocommand may also require a redraw, so it's done
+ // again below, unfortunately.
+ if (syntax_present(curwin) && must_redraw) {
update_screen(0);
- if (has_cursormovedI())
- apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, FALSE, curbuf);
+ }
+ if (has_event(EVENT_CURSORMOVEDI)) {
+ apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, false, curbuf);
+ }
if (curwin->w_p_cole > 0) {
conceal_old_cursor_line = last_cursormoved.lnum;
conceal_new_cursor_line = curwin->w_cursor.lnum;
@@ -1378,13 +1375,13 @@ ins_redraw (
last_cursormoved = curwin->w_cursor;
}
- /* Trigger TextChangedI if b_changedtick differs. */
- if (ready && has_textchangedI()
+ // Trigger TextChangedI if b_changedtick differs.
+ if (ready && has_event(EVENT_TEXTCHANGEDI)
&& last_changedtick != curbuf->b_changedtick
- && !pum_visible()
- ) {
- if (last_changedtick_buf == curbuf)
- apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, FALSE, curbuf);
+ && !pum_visible()) {
+ if (last_changedtick_buf == curbuf) {
+ apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf);
+ }
last_changedtick_buf = curbuf;
last_changedtick = curbuf->b_changedtick;
}
@@ -5124,24 +5121,20 @@ insertchar (
can_si = FALSE;
can_si_back = FALSE;
- /*
- * If there's any pending input, grab up to INPUT_BUFLEN at once.
- * This speeds up normal text input considerably.
- * Don't do this when 'cindent' or 'indentexpr' is set, because we might
- * need to re-indent at a ':', or any other character (but not what
- * 'paste' is set)..
- * Don't do this when there an InsertCharPre autocommand is defined,
- * because we need to fire the event for every character.
- */
-
- if ( !ISSPECIAL(c)
- && (!has_mbyte || (*mb_char2len)(c) == 1)
- && vpeekc() != NUL
- && !(State & REPLACE_FLAG)
- && !cindent_on()
- && !p_ri
- && !has_insertcharpre()
- ) {
+ // If there's any pending input, grab up to INPUT_BUFLEN at once.
+ // This speeds up normal text input considerably.
+ // Don't do this when 'cindent' or 'indentexpr' is set, because we might
+ // need to re-indent at a ':', or any other character (but not what
+ // 'paste' is set)..
+ // Don't do this when there an InsertCharPre autocommand is defined,
+ // because we need to fire the event for every character.
+ if (!ISSPECIAL(c)
+ && (!has_mbyte || (*mb_char2len)(c) == 1)
+ && vpeekc() != NUL
+ && !(State & REPLACE_FLAG)
+ && !cindent_on()
+ && !p_ri
+ && !has_event(EVENT_INSERTCHARPRE)) {
#define INPUT_BUFLEN 100
char_u buf[INPUT_BUFLEN + 1];
int i;
@@ -8486,13 +8479,13 @@ static char_u *do_insert_char_pre(int c)
{
char_u buf[MB_MAXBYTES + 1];
- /* Return quickly when there is nothing to do. */
- if (!has_insertcharpre())
+ // Return quickly when there is nothing to do.
+ if (!has_event(EVENT_INSERTCHARPRE)) {
return NULL;
-
- if (has_mbyte)
+ }
+ if (has_mbyte) {
buf[(*mb_char2bytes)(c, buf)] = NUL;
- else {
+ } else {
buf[0] = c;
buf[1] = NUL;
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index fad497928c..84bd31d9ad 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1694,7 +1694,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
// autocommands defined, trigger the matching autocommands.
if (p != NULL && ea.cmdidx == CMD_SIZE && !ea.skip
&& ASCII_ISUPPER(*ea.cmd)
- && has_cmdundefined()) {
+ && has_event(EVENT_CMDUNDEFINED)) {
p = ea.cmd;
while (ASCII_ISALNUM(*p)) {
++p;
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 05cb8f8f8c..bd0f0fc80a 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -6266,50 +6266,12 @@ int trigger_cursorhold(void)
return FALSE;
}
-/*
- * Return TRUE when there is a CursorMoved autocommand defined.
- */
-int has_cursormoved(void)
-{
- return first_autopat[(int)EVENT_CURSORMOVED] != NULL;
-}
-
-/*
- * Return TRUE when there is a CursorMovedI autocommand defined.
- */
-int has_cursormovedI(void)
-{
- return first_autopat[(int)EVENT_CURSORMOVEDI] != NULL;
-}
-
-/*
- * Return TRUE when there is a TextChanged autocommand defined.
- */
-int has_textchanged(void)
-{
- return first_autopat[(int)EVENT_TEXTCHANGED] != NULL;
-}
-
-/*
- * Return TRUE when there is a TextChangedI autocommand defined.
- */
-int has_textchangedI(void)
-{
- return first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL;
-}
-
-/*
- * Return TRUE when there is an InsertCharPre autocommand defined.
- */
-int has_insertcharpre(void)
-{
- return first_autopat[(int)EVENT_INSERTCHARPRE] != NULL;
-}
-
-/// @returns true when there is an CmdUndefined autocommand defined.
-int has_cmdundefined(void)
+/// Return true if "event" autocommand is defined.
+///
+/// @param event the autocommand to check
+bool has_event(int event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return first_autopat[(int)EVENT_CMDUNDEFINED] != NULL;
+ return first_autopat[event] != NULL;
}
static int
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index 04d6cbf5e3..b2fd929714 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -18,6 +18,9 @@
#include "nvim/strings.h"
#include "nvim/mouse.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "keymap.c.generated.h"
+#endif
/*
* Some useful tables.
@@ -637,11 +640,11 @@ find_special_key (
key = DEL;
}
- /*
- * Normal Key with modifier: Try to make a single byte code.
- */
- if (!IS_SPECIAL(key))
+ // Normal Key with modifier:
+ // Try to make a single byte code (except for Alt/Meta modifiers).
+ if (!IS_SPECIAL(key)) {
key = extract_modifiers(key, &modifiers);
+ }
*modp = modifiers;
*srcp = end_of_name;
@@ -652,11 +655,9 @@ find_special_key (
return 0;
}
-/*
- * Try to include modifiers in the key.
- * Changes "Shift-a" to 'A', "Alt-A" to 0xc0, etc.
- */
-int extract_modifiers(int key, int *modp)
+/// Try to include modifiers (except alt/meta) in the key.
+/// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc.
+static int extract_modifiers(int key, int *modp)
{
int modifiers = *modp;
@@ -665,19 +666,12 @@ int extract_modifiers(int key, int *modp)
modifiers &= ~MOD_MASK_SHIFT;
}
if ((modifiers & MOD_MASK_CTRL)
- && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))
- ) {
+ && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) {
key = Ctrl_chr(key);
modifiers &= ~MOD_MASK_CTRL;
- /* <C-@> is <Nul> */
- if (key == 0)
+ if (key == 0) { // <C-@> is <Nul>
key = K_ZERO;
- }
- if ((modifiers & MOD_MASK_ALT) && key < 0x80
- && !enc_dbcs /* avoid creating a lead byte */
- ) {
- key |= 0x80;
- modifiers &= ~MOD_MASK_ALT; /* remove the META modifier */
+ }
}
*modp = modifiers;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index d4bf1c2e90..2f57d8c610 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1205,9 +1205,9 @@ static void normal_check_interrupt(NormalState *s)
static void normal_check_cursor_moved(NormalState *s)
{
// Trigger CursorMoved if the cursor moved.
- if (!finish_op && (has_cursormoved() || curwin->w_p_cole > 0)
+ if (!finish_op && (has_event(EVENT_CURSORMOVED) || curwin->w_p_cole > 0)
&& !equalpos(last_cursormoved, curwin->w_cursor)) {
- if (has_cursormoved()) {
+ if (has_event(EVENT_CURSORMOVED)) {
apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
}
@@ -1224,7 +1224,7 @@ static void normal_check_cursor_moved(NormalState *s)
static void normal_check_text_changed(NormalState *s)
{
// Trigger TextChanged if b_changedtick differs.
- if (!finish_op && has_textchanged()
+ if (!finish_op && has_event(EVENT_TEXTCHANGED)
&& last_changedtick != curbuf->b_changedtick) {
if (last_changedtick_buf == curbuf) {
apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf);
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 1a4c3495f2..21f0fc6f41 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -151,7 +151,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
// Walk through all entries in $PATH to check if "name" exists there and
// is an executable file.
for (;; ) {
- const char *e = xstrchrnul(path, ':');
+ const char *e = xstrchrnul(path, ENV_SEPCHAR);
// Glue together the given directory from $PATH with name and save into
// buf.
@@ -169,7 +169,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
return true;
}
- if (*e != ':') {
+ if (*e != ENV_SEPCHAR) {
// End of $PATH without finding any executable called name.
xfree(buf);
return false;
diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h
index 79d14920eb..8afbd29292 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -13,24 +13,7 @@
# include "nvim/os/unix_defs.h"
#endif
-#if defined(DIRSIZ) && !defined(MAXNAMLEN)
-# define MAXNAMLEN DIRSIZ
-#endif
-
-#if defined(UFS_MAXNAMLEN) && !defined(MAXNAMLEN)
-# define MAXNAMLEN UFS_MAXNAMLEN /* for dynix/ptx */
-#endif
-
-#if defined(NAME_MAX) && !defined(MAXNAMLEN)
-# define MAXNAMLEN NAME_MAX /* for Linux before .99p3 */
-#endif
-
-// Default value.
-#ifndef MAXNAMLEN
-# define MAXNAMLEN 512
-#endif
-
-#define BASENAMELEN (MAXNAMLEN - 5)
+#define BASENAMELEN (NAME_MAX - 5)
// Use the system path length if it makes sense.
#if defined(PATH_MAX) && (PATH_MAX > 1000)
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 3813c45726..f5a1637c94 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -36,7 +36,6 @@ typedef struct {
# include "os/shell.c.generated.h"
#endif
-
/// Builds the argument vector for running the user-configured 'shell' (p_sh)
/// with an optional command prefixed by 'shellcmdflag' (p_shcf).
///
@@ -45,9 +44,10 @@ typedef struct {
/// @return A newly allocated argument vector. It must be freed with
/// `shell_free_argv` when no longer needed.
char **shell_build_argv(const char *cmd, const char *extra_args)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC
{
- size_t argc = tokenize(p_sh, NULL) + tokenize(p_shcf, NULL);
- char **rv = xmalloc((unsigned)((argc + 4) * sizeof(char *)));
+ size_t argc = tokenize(p_sh, NULL) + (cmd ? tokenize(p_shcf, NULL) : 0);
+ char **rv = xmalloc((argc + 4) * sizeof(*rv));
// Split 'shell'
size_t i = tokenize(p_sh, rv);
@@ -338,24 +338,22 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data,
/// @param argv The vector that will be filled with copies of the parsed
/// words. It can be NULL if the caller only needs to count words.
/// @return The number of words parsed.
-static size_t tokenize(const char_u *str, char **argv)
+static size_t tokenize(const char_u *const str, char **const argv)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- size_t argc = 0, len;
- char_u *p = (char_u *) str;
+ size_t argc = 0;
+ const char *p = (const char *) str;
while (*p != NUL) {
- len = word_length(p);
+ const size_t len = word_length((const char_u *) p);
if (argv != NULL) {
// Fill the slot
- argv[argc] = xmalloc(len + 1);
- memcpy(argv[argc], p, len);
- argv[argc][len] = NUL;
+ argv[argc] = vim_strnsave_unquoted(p, len);
}
argc++;
- p += len;
- p = skipwhite(p);
+ p = (const char *) skipwhite((char_u *) (p + len));
}
return argc;
@@ -377,6 +375,9 @@ static size_t word_length(const char_u *str)
if (*p == '"') {
// Found a quote character, switch the `inquote` flag
inquote = !inquote;
+ } else if (*p == '\\' && inquote) {
+ p++;
+ length++;
}
p++;
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index e3ba3262f4..78fc9331d1 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -1,13 +1,12 @@
#ifndef NVIM_OS_UNIX_DEFS_H
#define NVIM_OS_UNIX_DEFS_H
+// Windows doesn't have unistd.h, so we include it here to avoid numerous
+// instances of `#ifdef HAVE_UNISTD_H'.
#include <unistd.h>
-#include <signal.h>
-// Defines BSD, if it's a BSD system.
-#ifdef HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
+// POSIX.1-2008 says that NAME_MAX should be in here
+#include <limits.h>
#define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "~"}
#define TEMP_FILE_PATH_MAXLEN 256
@@ -17,4 +16,7 @@
// Special wildcards that need to be handled by the shell.
#define SPECIAL_WILDCHAR "`'{"
+// Separator character for environment variables.
+#define ENV_SEPCHAR ':'
+
#endif // NVIM_OS_UNIX_DEFS_H
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index 9f9e5e277c..e6a714601f 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -5,11 +5,16 @@
#include <sys/stat.h>
#include <stdio.h>
+#define NAME_MAX _MAX_PATH
+
#define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""}
#define TEMP_FILE_PATH_MAXLEN _MAX_PATH
#define FNAME_ILLEGAL "\"*?><|"
+// Separator character for environment variables.
+#define ENV_SEPCHAR ';'
+
#define USE_CRNL
#ifdef _MSC_VER
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 62b264046c..6c7cb3bba7 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -44,10 +44,6 @@
#include "nvim/os/signal.h"
#include "nvim/msgpack_rpc/helpers.h"
-#ifdef HAVE_STROPTS_H
-# include <stropts.h>
-#endif
-
#ifdef HAVE_SELINUX
# include <selinux/selinux.h>
static int selinux_enabled = -1;
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 00dcf3cf46..fe91141375 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -119,6 +119,54 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars,
return escaped_string;
}
+/// Save a copy of an unquoted string
+///
+/// Turns string like `a\bc"def\"ghi\\\n"jkl` into `a\bcdef"ghi\\njkl`, for use
+/// in shell_build_argv: the only purpose of backslash is making next character
+/// be treated literally inside the double quotes, if this character is
+/// backslash or quote.
+///
+/// @param[in] string String to copy.
+/// @param[in] length Length of the string to copy.
+///
+/// @return [allocated] Copy of the string.
+char *vim_strnsave_unquoted(const char *const string, const size_t length)
+ FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_RET
+{
+#define ESCAPE_COND(p, inquote, string_end) \
+ (*p == '\\' && inquote && p + 1 < string_end && (p[1] == '\\' || p[1] == '"'))
+ size_t ret_length = 0;
+ bool inquote = false;
+ const char *const string_end = string + length;
+ for (const char *p = string; p < string_end; p++) {
+ if (*p == '"') {
+ inquote = !inquote;
+ } else if (ESCAPE_COND(p, inquote, string_end)) {
+ ret_length++;
+ p++;
+ } else {
+ ret_length++;
+ }
+ }
+
+ char *const ret = xmallocz(ret_length);
+ char *rp = ret;
+ inquote = false;
+ for (const char *p = string; p < string_end; p++) {
+ if (*p == '"') {
+ inquote = !inquote;
+ } else if (ESCAPE_COND(p, inquote, string_end)) {
+ *rp++ = *(++p);
+ } else {
+ *rp++ = *p;
+ }
+ }
+#undef ESCAPE_COND
+
+ return ret;
+}
+
/*
* Escape "string" for use as a shell argument with system().
* This uses single quotes, except when we know we need to use double quotes
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index adf3f725a2..6045acd1cb 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -1,5 +1,5 @@
-// VT220/xterm-like terminal emulator implementation for Neovim. Powered by
-// libvterm(http://www.leonerd.org.uk/code/libvterm/).
+// VT220/xterm-like terminal emulator implementation for nvim. Powered by
+// libvterm (http://www.leonerd.org.uk/code/libvterm/).
//
// libvterm is a pure C99 terminal emulation library with abstract input and
// display. This means that the library needs to read data from the master fd
@@ -10,31 +10,31 @@
// vterm_keyboard_key/vterm_keyboard_unichar, which generates byte streams that
// must be fed back to the master fd.
//
-// This implementation uses Neovim buffers as the display mechanism for both
+// This implementation uses nvim buffers as the display mechanism for both
// the visible screen and the scrollback buffer. When focused, the window
// "pins" to the bottom of the buffer and mirrors libvterm screen state.
//
// When a line becomes invisible due to a decrease in screen height or because
// a line was pushed up during normal terminal output, we store the line
-// information in the scrollback buffer, which is mirrored in the Neovim buffer
+// information in the scrollback buffer, which is mirrored in the nvim buffer
// by appending lines just above the visible part of the buffer.
//
// When the screen height increases, libvterm will ask for a row in the
-// scrollback buffer, which is mirrored in the Neovim buffer displaying lines
+// scrollback buffer, which is mirrored in the nvim buffer displaying lines
// that were previously invisible.
//
-// The vterm->Neovim synchronization is performed in intervals of 10
+// The vterm->nvim synchronization is performed in intervals of 10
// milliseconds. This is done to minimize screen updates when receiving large
// bursts of data.
//
// This module is decoupled from the processes that normally feed it data, so
-// it's possible to use it as a general purpose console buffer(possibly as a
-// log/display mechanism for Neovim in the future)
+// it's possible to use it as a general purpose console buffer (possibly as a
+// log/display mechanism for nvim in the future)
//
-// Inspired by vimshell(http://www.wana.at/vimshell/) and
-// Conque(https://code.google.com/p/conque/). Libvterm usage instructions (plus
+// Inspired by vimshell (http://www.wana.at/vimshell/) and
+// Conque (https://code.google.com/p/conque/). Libvterm usage instructions (plus
// some extra code) were taken from
-// pangoterm(http://www.leonerd.org.uk/code/pangoterm/)
+// pangoterm (http://www.leonerd.org.uk/code/pangoterm/)
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
@@ -402,11 +402,11 @@ static int terminal_execute(VimState *state, int key)
TerminalState *s = (TerminalState *)state;
switch (key) {
- case K_FOCUSGAINED: // Neovim has been given focus
+ case K_FOCUSGAINED: // nvim has been given focus
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
break;
- case K_FOCUSLOST: // Neovim has lost focus
+ case K_FOCUSLOST: // nvim has lost focus
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
break;
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 14700a2622..838d05a6df 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -53,7 +53,7 @@ describe('tui', function()
]])
end)
- it('interprets leading esc byte as the alt modifier', function()
+ it('interprets leading <Esc> byte as ALT modifier in normal-mode', function()
local keys = 'dfghjkl'
for c in keys:gmatch('.') do
execute('nnoremap <a-'..c..'> ialt-'..c..'<cr><esc>')
@@ -80,6 +80,25 @@ describe('tui', function()
]])
end)
+ it('does not mangle unmapped ALT-key chord', function()
+ -- Vim represents ALT/META by setting the "high bit" of the modified key;
+ -- we do _not_. #3982
+ --
+ -- Example: for input ALT+j:
+ -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê".
+ -- * Nvim (after #3982) inserts "j".
+ feed('i\x1bj')
+ screen:expect([[
+ j{1: } |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] [+] |
+ -- INSERT -- |
+ -- TERMINAL -- |
+ ]])
+ end)
+
it('accepts ascii control sequences', function()
feed('i')
feed('\x16\x07') -- ctrl+g
diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua
index 01deefab0c..6d1a9f3589 100644
--- a/test/unit/os/shell_spec.lua
+++ b/test/unit/os/shell_spec.lua
@@ -15,7 +15,8 @@ local shell = helpers.cimport(
'./src/nvim/os/shell.h',
'./src/nvim/option_defs.h',
'./src/nvim/main.h',
- './src/nvim/misc1.h'
+ './src/nvim/misc1.h',
+ './src/nvim/memory.h'
)
local ffi, eq = helpers.ffi, helpers.eq
local intern = helpers.internalize
@@ -34,6 +35,23 @@ describe('shell functions', function()
shell.event_teardown()
end)
+ local function shell_build_argv(cmd, extra_args)
+ local res = shell.shell_build_argv(
+ cmd and to_cstr(cmd),
+ extra_args and to_cstr(extra_args))
+ local argc = 0
+ local ret = {}
+ -- Explicitly free everything, so if it is not in allocated memory it will
+ -- crash.
+ while res[argc] ~= nil do
+ ret[#ret + 1] = ffi.string(res[argc])
+ shell.xfree(res[argc])
+ argc = argc + 1
+ end
+ shell.xfree(res)
+ return ret
+ end
+
local function os_system(cmd, input)
local input_or = input and to_cstr(input) or NULL
local input_len = (input ~= nil) and string.len(input) or 0
@@ -74,4 +92,44 @@ describe('shell functions', function()
eq(2, status)
end)
end)
+
+ describe('shell_build_argv', function()
+ local saved_opts = {}
+
+ setup(function()
+ saved_opts.p_sh = shell.p_sh
+ saved_opts.p_shcf = shell.p_shcf
+ end)
+
+ teardown(function()
+ shell.p_sh = saved_opts.p_sh
+ shell.p_shcf = saved_opts.p_shcf
+ end)
+
+ it('works with NULL arguments', function()
+ eq({'/bin/bash'}, shell_build_argv(nil, nil))
+ end)
+
+ it('works with cmd', function()
+ eq({'/bin/bash', '-c', 'abc def'}, shell_build_argv('abc def', nil))
+ end)
+
+ it('works with extra_args', function()
+ eq({'/bin/bash', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl'))
+ end)
+
+ it('works with cmd and extra_args', function()
+ eq({'/bin/bash', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl'))
+ end)
+
+ it('splits and unquotes &shell and &shellcmdflag', function()
+ shell.p_sh = to_cstr('/Program" "Files/zsh -f')
+ shell.p_shcf = to_cstr('-x -o "sh word split" "-"c')
+ eq({'/Program Files/zsh', '-f',
+ 'ghi jkl',
+ '-x', '-o', 'sh word split',
+ '-c', 'abc def'},
+ shell_build_argv('abc def', 'ghi jkl'))
+ end)
+ end)
end)
diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua
new file mode 100644
index 0000000000..b310ccb541
--- /dev/null
+++ b/test/unit/strings_spec.lua
@@ -0,0 +1,69 @@
+local helpers = require("test.unit.helpers")
+
+local cimport = helpers.cimport
+local internalize = helpers.internalize
+local eq = helpers.eq
+local ffi = helpers.ffi
+local to_cstr = helpers.to_cstr
+
+local strings = cimport('stdlib.h', './src/nvim/strings.h',
+ './src/nvim/memory.h')
+
+describe('vim_strnsave_unquoted()', function()
+ local vim_strnsave_unquoted = function(s, len)
+ local res = strings.vim_strnsave_unquoted(to_cstr(s), len or #s)
+ local ret = ffi.string(res)
+ -- Explicitly free memory so we are sure it is allocated: if it was not it
+ -- will crash.
+ strings.xfree(res)
+ return ret
+ end
+
+ it('copies unquoted strings as-is', function()
+ eq('-c', vim_strnsave_unquoted('-c'))
+ eq('', vim_strnsave_unquoted(''))
+ end)
+
+ it('respects length argument', function()
+ eq('', vim_strnsave_unquoted('-c', 0))
+ eq('-', vim_strnsave_unquoted('-c', 1))
+ eq('-', vim_strnsave_unquoted('"-c', 2))
+ end)
+
+ it('unquotes fully quoted word', function()
+ eq('/bin/sh', vim_strnsave_unquoted('"/bin/sh"'))
+ end)
+
+ it('unquotes partially quoted word', function()
+ eq('/Program Files/sh', vim_strnsave_unquoted('/Program" "Files/sh'))
+ end)
+
+ it('removes ""', function()
+ eq('/Program Files/sh', vim_strnsave_unquoted('/""Program" "Files/sh'))
+ end)
+
+ it('performs unescaping of "', function()
+ eq('/"Program Files"/sh', vim_strnsave_unquoted('/"\\""Program Files"\\""/sh'))
+ end)
+
+ it('performs unescaping of \\', function()
+ eq('/\\Program Files\\foo/sh', vim_strnsave_unquoted('/"\\\\"Program Files"\\\\foo"/sh'))
+ end)
+
+ it('strips quote when there is no pair to it', function()
+ eq('/Program Files/sh', vim_strnsave_unquoted('/Program" Files/sh'))
+ eq('', vim_strnsave_unquoted('"'))
+ end)
+
+ it('allows string to end with one backslash unescaped', function()
+ eq('/Program Files/sh\\', vim_strnsave_unquoted('/Program" Files/sh\\'))
+ end)
+
+ it('does not perform unescaping out of quotes', function()
+ eq('/Program\\ Files/sh\\', vim_strnsave_unquoted('/Program\\ Files/sh\\'))
+ end)
+
+ it('does not unescape \\n', function()
+ eq('/Program\\nFiles/sh', vim_strnsave_unquoted('/Program"\\n"Files/sh'))
+ end)
+end)