aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md29
-rw-r--r--runtime/autoload/health.vim2
-rw-r--r--runtime/autoload/health/provider.vim12
-rw-r--r--runtime/autoload/provider.vim2
-rw-r--r--runtime/autoload/provider/clipboard.vim20
-rw-r--r--runtime/doc/api.txt16
-rw-r--r--runtime/doc/eval.txt39
-rw-r--r--runtime/doc/vim_diff.txt32
-rw-r--r--src/nvim/api/vim.c10
-rw-r--r--src/nvim/eval.c18
-rw-r--r--src/nvim/main.c71
-rw-r--r--src/nvim/ops.c72
-rw-r--r--src/nvim/testdir/test_startup.vim6
-rw-r--r--src/nvim/version.c13
-rw-r--r--test/functional/clipboard/clipboard_provider_spec.lua67
-rw-r--r--test/functional/fixtures/autoload/provider/clipboard.vim8
16 files changed, 266 insertions, 151 deletions
diff --git a/README.md b/README.md
index 0442ee61de..4016c9b3c2 100644
--- a/README.md
+++ b/README.md
@@ -48,17 +48,24 @@ and [more](https://github.com/neovim/neovim/wiki/Installing-Neovim)!
Project layout
--------------
- ├─ ci/ Build server scripts
- ├─ cmake/ Build scripts
- ├─ runtime/ User plugins/docs
- ├─ src/ Source code
- ├─ third-party/ CMake subproject to build dependencies
- └─ test/ Test code
-
-- `third-party/` is activated if `USE_BUNDLED_DEPS` is undefined or the
- `USE_BUNDLED` CMake option is true.
-- [Source README](src/nvim/README.md)
-- [Test README](test/README.md)
+ ├─ ci/ build automation
+ ├─ cmake/ build scripts
+ ├─ runtime/ user plugins/docs
+ ├─ src/ application source code (see src/nvim/README.md)
+ │ ├─ api/ API subsystem
+ │ ├─ eval/ VimL subsystem
+ │ ├─ event/ event-loop subsystem
+ │ ├─ generators/ code generation (pre-compilation)
+ │ ├─ lib/ generic data structures
+ │ ├─ lua/ lua subsystem
+ │ ├─ msgpack_rpc/ RPC subsystem
+ │ ├─ os/ low-level platform code
+ │ └─ tui/ built-in UI
+ ├─ third-party/ cmake subproject to build dependencies
+ └─ test/ tests (see test/README.md)
+
+- To disable `third-party/` specify `USE_BUNDLED_DEPS=NO` or `USE_BUNDLED=NO`
+ (CMake option).
Features
--------
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim
index f875c8b797..bd99a0e104 100644
--- a/runtime/autoload/health.vim
+++ b/runtime/autoload/health.vim
@@ -90,7 +90,7 @@ endfunction
" Changes ':h clipboard' to ':help |clipboard|'.
function! s:help_to_link(s) abort
- return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n]+)', ':help |\1|', 'g')
+ return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n ]+)', ':help |\1|', 'g')
endfunction
" Format a message for a specific report item
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index ec20615f69..26db5b77b7 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -121,14 +121,14 @@ function! s:check_clipboard() abort
call health#report_start('Clipboard (optional)')
let clipboard_tool = provider#clipboard#Executable()
- if empty(clipboard_tool)
+ if exists('g:clipboard') && empty(clipboard_tool)
+ call health#report_error(
+ \ provider#clipboard#Error(),
+ \ ["Use the example in :help g:clipboard as a template, or don't set g:clipboard at all."])
+ elseif empty(clipboard_tool)
call health#report_warn(
- \ 'No clipboard tool found. Clipboard registers will not work.',
+ \ 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.',
\ [':help clipboard'])
- elseif exists('g:clipboard') && (type({}) != type(g:clipboard)
- \ || !has_key(g:clipboard, 'copy') || !has_key(g:clipboard, 'paste'))
- call health#report_error(
- \ 'g:clipboard exists but is malformed. It must be a dictionary with the keys documented at :help g:clipboard')
else
call health#report_ok('Clipboard tool found: '. clipboard_tool)
endif
diff --git a/runtime/autoload/provider.vim b/runtime/autoload/provider.vim
index a4d5241b57..b46ae12b3c 100644
--- a/runtime/autoload/provider.vim
+++ b/runtime/autoload/provider.vim
@@ -10,7 +10,7 @@ function! provider#stderr_collector(chan_id, data, event) dict
endfunction
function! provider#clear_stderr(chan_id)
- silent! call delete(s:stderr, a:chan_id)
+ silent! call remove(s:stderr, a:chan_id)
endfunction
function! provider#get_stderr(chan_id)
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 8eb694e9fa..8fe53c495a 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -3,6 +3,7 @@
" available.
let s:copy = {}
let s:paste = {}
+let s:clipboard = {}
" When caching is enabled, store the jobid of the xclip/xsel process keeping
" ownership of the selection, so we know how long the cache is valid.
@@ -23,7 +24,7 @@ function! s:selection.on_exit(jobid, data, event) abort
call provider#clear_stderr(a:jobid)
endfunction
-let s:selections = { '*': s:selection, '+': copy(s:selection)}
+let s:selections = { '*': s:selection, '+': copy(s:selection) }
function! s:try_cmd(cmd, ...) abort
let argv = split(a:cmd, " ")
@@ -55,9 +56,15 @@ endfunction
function! provider#clipboard#Executable() abort
if exists('g:clipboard')
+ if type({}) isnot# type(g:clipboard)
+ \ || type({}) isnot# type(get(g:clipboard, 'copy', v:null))
+ \ || type({}) isnot# type(get(g:clipboard, 'paste', v:null))
+ let s:err = 'clipboard: invalid g:clipboard'
+ return ''
+ endif
let s:copy = get(g:clipboard, 'copy', { '+': v:null, '*': v:null })
let s:paste = get(g:clipboard, 'paste', { '+': v:null, '*': v:null })
- let s:cache_enabled = get(g:clipboard, 'cache_enabled', 1)
+ let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0)
return get(g:clipboard, 'name', 'g:clipboard')
elseif has('mac') && executable('pbcopy')
let s:copy['+'] = 'pbcopy'
@@ -104,16 +111,17 @@ function! provider#clipboard#Executable() abort
return 'tmux'
endif
- let s:err = 'clipboard: No clipboard tool available. :help clipboard'
+ let s:err = 'clipboard: No clipboard tool. :help clipboard'
return ''
endfunction
if empty(provider#clipboard#Executable())
+ " provider#clipboard#Call() *must not* be defined if the provider is broken.
+ " Otherwise eval_has_provider() thinks the clipboard provider is
+ " functioning, and eval_call_provider() will happily call it.
finish
endif
-let s:clipboard = {}
-
function! s:clipboard.get(reg) abort
if s:selections[a:reg].owner > 0
return s:selections[a:reg].data
@@ -154,7 +162,9 @@ function! s:clipboard.set(lines, regtype, reg) abort
echohl WarningMsg
echomsg 'clipboard: failed to execute: '.(s:copy[a:reg])
echohl None
+ return 0
endif
+ return 1
endfunction
function! provider#clipboard#Call(method, args) abort
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 7c6b8a3c1a..e12cd1cfa9 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -171,8 +171,8 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
Parameters:~
{str} String to be converted.
{from_part} Legacy Vim parameter. Usually true.
- {do_lt} Also translate <lt>. Does nothing if
- `special` is false.
+ {do_lt} Also translate <lt>. Ignored if `special` is
+ false.
{special} Replace |keycodes|, e.g. <CR> becomes a "\n"
char.
@@ -309,20 +309,24 @@ nvim_set_option({name}, {value}) *nvim_set_option()*
{value} New option value
nvim_out_write({str}) *nvim_out_write()*
- Writes a message to vim output buffer
+ Writes a message to the Vim output buffer. Does not append
+ "\n", the message is buffered (won't display) until a linefeed
+ is written.
Parameters:~
{str} Message
nvim_err_write({str}) *nvim_err_write()*
- Writes a message to vim error buffer
+ Writes a message to the Vim error buffer. Does not append
+ "\n", the message is buffered (won't display) until a linefeed
+ is written.
Parameters:~
{str} Message
nvim_err_writeln({str}) *nvim_err_writeln()*
- Writes a message to vim error buffer. Appends a linefeed to
- ensure all contents are written.
+ Writes a message to the Vim error buffer. Appends "\n", so the
+ buffer is flushed (and displayed).
Parameters:~
{str} Message
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 29e254b0b3..b37b0e8836 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1522,14 +1522,16 @@ v:errors Errors found by assert functions, such as |assert_true()|.
*v:event* *event-variable*
v:event Dictionary of event data for the current |autocommand|. Valid
- only during the autocommand lifetime: storing or passing
- `v:event` is invalid. Copy it instead: >
+ only during the event lifetime; storing or passing v:event is
+ invalid! Copy it instead: >
au TextYankPost * let g:foo = deepcopy(v:event)
< Keys vary by event; see the documentation for the specific
- event, e.g. |TextYankPost|.
+ event, e.g. |DirChanged| or |TextYankPost|.
KEY DESCRIPTION ~
- operator The current |operator|. Also set for
- Ex commands (unlike |v:operator|). For
+ cwd Current working directory
+ scope Event-specific scope name.
+ operator Current |operator|. Also set for Ex
+ commands (unlike |v:operator|). For
example if |TextYankPost| is triggered
by the |:yank| Ex command then
`v:event['operator']` is "y".
@@ -4726,7 +4728,8 @@ input({opts})
"-complete=" argument. Refer to |:command-completion| for
more information. Example: >
let fname = input("File: ", "", "file")
-< *E5400* *E5402*
+
+< *input()-highlight* *E5400* *E5402*
The optional `highlight` key allows specifying function which
will be used for highlighting user input. This function
receives user input as its only argument and must return
@@ -4744,6 +4747,30 @@ input({opts})
sections must be ordered so that next hl_start_col is greater
then or equal to previous hl_end_col.
+ Example (try some input with parentheses): >
+ highlight RBP1 guibg=Red ctermbg=red
+ highlight RBP2 guibg=Yellow ctermbg=yellow
+ highlight RBP3 guibg=Green ctermbg=green
+ highlight RBP4 guibg=Blue ctermbg=blue
+ let g:rainbow_levels = 4
+ function! RainbowParens(cmdline)
+ let ret = []
+ let i = 0
+ let lvl = 0
+ while i < len(a:cmdline)
+ if a:cmdline[i] is# '('
+ call add(ret, [i, i + 1, 'RBP' . ((lvl % g:rainbow_levels) + 1)])
+ let lvl += 1
+ elseif a:cmdline[i] is# ')'
+ let lvl -= 1
+ call add(ret, [i, i + 1, 'RBP' . ((lvl % g:rainbow_levels) + 1)])
+ endif
+ let i += 1
+ endwhile
+ return ret
+ endfunction
+ call input({'prompt':'>','highlight':'RainbowParens'})
+<
Highlight function is called at least once for each new
displayed input string, before command-line is redrawn. It is
expected that function is pure for the duration of one input()
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 2f031c0b1f..eb3c4d50ce 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -6,9 +6,8 @@
Differences between Nvim and Vim *vim-differences*
-Throughout the help files, differences between Nvim and Vim are indicated via
-the "{Nvim}" tag. This document is a complete and centralized list of all
-these differences.
+Nvim differs from Vim in many ways, big and small. This document is
+a complete and centralized reference of those differences.
Type <M-]> to see the table of contents.
@@ -72,12 +71,18 @@ Clipboard integration |provider-clipboard|
USER EXPERIENCE ~
-Working intuitively and consistently is a major goal of Nvim. Examples:
+Working intuitively and consistently is a major goal of Nvim.
-- Nvim does not have `-X`, a platform-specific option "sometimes" available in
- Vim (with potential surprises: http://stackoverflow.com/q/14635295). Nvim
- avoids features that cannot be provided on all platforms--instead that is
- delegated to external plugins/extensions.
+ *feature-compile*
+- Nvim always includes ALL features, in contrast to Vim (which ships with
+ various combinations of 100+ optional features). Think of it as a leaner
+ version of Vim's "HUGE" build. This reduces surface area for bugs, and
+ removes a common source of confusion and friction for users.
+
+- Nvim avoids features that cannot be provided on all platforms; instead that
+ is delegated to external plugins/extensions. E.g. the `-X` platform-specific
+ option is "sometimes" available in Vim (with potential surprises:
+ http://stackoverflow.com/q/14635295).
- Vim's internal test functions (test_autochdir(), test_settime(), etc.) are
not exposed (nor implemented); instead Nvim has a robust API.
@@ -268,13 +273,12 @@ Lua interface (|if_lua.txt|):
- Lua has direct access to Nvim |API| via `vim.api`.
- Lua package.path and package.cpath are automatically updated according to
'runtimepath': |lua-require|.
-- Currently, most legacy Vim features are missing.
-|input()| and |inputdialog()| gained support for each other’s features (return
-on cancel and completion respectively) via dictionary argument (replaces all
+|input()| and |inputdialog()| support for each other’s features (return on
+cancel and completion respectively) via dictionary argument (replaces all
other arguments if used).
-|input()| and |inputdialog()| now support user-defined cmdline highlighting.
+|input()| and |inputdialog()| support user-defined cmdline highlighting.
==============================================================================
5. Missing legacy features *nvim-features-missing*
@@ -282,7 +286,7 @@ other arguments if used).
Some legacy Vim features are not implemented:
- |if_py|: vim.bindeval() and vim.Function() are not supported
-- |if_lua|: the `vim` object currently only supports `vim.api`
+- |if_lua|: the `vim` object is missing most legacy methods
- *if_perl*
- *if_mzscheme*
- *if_tcl*
@@ -290,7 +294,7 @@ Some legacy Vim features are not implemented:
==============================================================================
6. Removed features *nvim-features-removed*
-These features are in Vim, but have been intentionally removed from Nvim.
+These Vim features were intentionally removed from Nvim.
*'cp'* *'nocompatible'* *'nocp'* *'compatible'*
Nvim is always "non-compatible" with Vi.
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 2bc31b2812..cfbe34b848 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -484,7 +484,8 @@ void nvim_set_option(String name, Object value, Error *err)
set_option_to(NULL, SREQ_GLOBAL, name, value, err);
}
-/// Writes a message to vim output buffer
+/// Writes a message to the Vim output buffer. Does not append "\n", the
+/// message is buffered (won't display) until a linefeed is written.
///
/// @param str Message
void nvim_out_write(String str)
@@ -493,7 +494,8 @@ void nvim_out_write(String str)
write_msg(str, false);
}
-/// Writes a message to vim error buffer
+/// Writes a message to the Vim error buffer. Does not append "\n", the
+/// message is buffered (won't display) until a linefeed is written.
///
/// @param str Message
void nvim_err_write(String str)
@@ -502,8 +504,8 @@ void nvim_err_write(String str)
write_msg(str, true);
}
-/// Writes a message to vim error buffer. Appends a linefeed to ensure all
-/// contents are written.
+/// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
+/// flushed (and displayed).
///
/// @param str Message
/// @see nvim_err_write()
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ac22d75a83..d6ee13857a 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -22775,7 +22775,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
bool eval_has_provider(const char *name)
{
-#define check_provider(name) \
+#define CHECK_PROVIDER(name) \
if (has_##name == -1) { \
has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \
if (!has_##name) { \
@@ -22791,17 +22791,17 @@ bool eval_has_provider(const char *name)
static int has_python3 = -1;
static int has_ruby = -1;
- if (!strcmp(name, "clipboard")) {
- check_provider(clipboard);
+ if (strequal(name, "clipboard")) {
+ CHECK_PROVIDER(clipboard);
return has_clipboard;
- } else if (!strcmp(name, "python3")) {
- check_provider(python3);
+ } else if (strequal(name, "python3")) {
+ CHECK_PROVIDER(python3);
return has_python3;
- } else if (!strcmp(name, "python")) {
- check_provider(python);
+ } else if (strequal(name, "python")) {
+ CHECK_PROVIDER(python);
return has_python;
- } else if (!strcmp(name, "ruby")) {
- check_provider(ruby);
+ } else if (strequal(name, "ruby")) {
+ CHECK_PROVIDER(ruby);
return has_ruby;
}
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 3f828d7be9..a665ad1de2 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -1878,54 +1878,47 @@ static void usage(void)
signal_stop(); // kill us with CTRL-C here, if you like
mch_msg(_("Usage:\n"));
- mch_msg(_(" nvim [arguments] [file ...] Edit specified file(s)\n"));
- mch_msg(_(" nvim [arguments] - Read text from stdin\n"));
- mch_msg(_(" nvim [arguments] -t <tag> Edit file where tag is defined\n"));
- mch_msg(_(" nvim [arguments] -q [errorfile] Edit file with first error\n"));
- mch_msg(_("\nArguments:\n"));
+ mch_msg(_(" nvim [options] [file ...] Edit file(s)\n"));
+ mch_msg(_(" nvim [options] - Read text from stdin\n"));
+ mch_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n"));
+ mch_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n"));
+ mch_msg(_("\nOptions:\n"));
mch_msg(_(" -- Only file names after this\n"));
-#if !defined(UNIX)
- mch_msg(_(" --literal Don't expand wildcards\n"));
-#endif
- mch_msg(_(" -e Ex mode\n"));
- mch_msg(_(" -E Improved Ex mode\n"));
- mch_msg(_(" -s Silent (batch) mode (only for ex mode)\n"));
+ mch_msg(_(" + Start at end of file\n"));
+ mch_msg(_(" --cmd <cmd> Execute <cmd> before any config\n"));
+ mch_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n"));
+ mch_msg("\n");
+ mch_msg(_(" -b Binary mode\n"));
mch_msg(_(" -d Diff mode\n"));
- mch_msg(_(" -R Read-only mode\n"));
- mch_msg(_(" -Z Restricted mode\n"));
+ mch_msg(_(" -e, -E Ex mode, Improved Ex mode\n"));
+ mch_msg(_(" -es Silent (batch) mode\n"));
+ mch_msg(_(" -h, --help Print this help message\n"));
+ mch_msg(_(" -i <shada> Use this shada file\n"));
mch_msg(_(" -m Modifications (writing files) not allowed\n"));
mch_msg(_(" -M Modifications in text not allowed\n"));
- mch_msg(_(" -b Binary mode\n"));
- mch_msg(_(" -l Lisp mode\n"));
- mch_msg(_(" -A Arabic mode\n"));
- mch_msg(_(" -F Farsi mode\n"));
- mch_msg(_(" -H Hebrew mode\n"));
- mch_msg(_(" -V[N][file] Be verbose [level N][log messages to file]\n"));
- mch_msg(_(" -D Debugging mode\n"));
mch_msg(_(" -n No swap file, use memory only\n"));
- mch_msg(_(" -r, -L List swap files and exit\n"));
- mch_msg(_(" -r <file> Recover crashed session\n"));
- mch_msg(_(" -u <vimrc> Use <vimrc> instead of the default\n"));
- mch_msg(_(" -i <shada> Use <shada> instead of the default\n"));
- mch_msg(_(" --noplugin Don't load plugin scripts\n"));
- mch_msg(_(" -o[N] Open N windows (default: one for each file)\n"));
- mch_msg(_(" -O[N] Like -o but split vertically\n"));
- mch_msg(_(" -p[N] Open N tab pages (default: one for each file)\n"));
- mch_msg(_(" + Start at end of file\n"));
- mch_msg(_(" +<linenum> Start at line <linenum>\n"));
- mch_msg(_(" +/<pattern> Start at first occurrence of <pattern>\n"));
- mch_msg(_(" --cmd <command> Execute <command> before loading any vimrc\n"));
- mch_msg(_(" -c <command> Execute <command> after loading the first file\n"));
+ mch_msg(_(" -o[N] Open N windows (default: one per file)\n"));
+ mch_msg(_(" -O[N] Open N vertical windows (default: one per file)\n"));
+ mch_msg(_(" -p[N] Open N tab pages (default: one per file)\n"));
+ mch_msg(_(" -r, -L List swap files\n"));
+ mch_msg(_(" -r <file> Recover edit state for this file\n"));
+ mch_msg(_(" -R Read-only mode\n"));
mch_msg(_(" -S <session> Source <session> after loading the first file\n"));
mch_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n"));
- mch_msg(_(" -w <scriptout> Append all typed characters to <scriptout>\n"));
- mch_msg(_(" -W <scriptout> Write all typed characters to <scriptout>\n"));
- mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n"));
- mch_msg(_(" --api-info Dump API metadata serialized to msgpack and exit\n"));
+ mch_msg(_(" -u <config> Use this config file\n"));
+ mch_msg(_(" -v, --version Print version information\n"));
+ mch_msg(_(" -V[N][file] Verbose [level][file]\n"));
+ mch_msg(_(" -Z Restricted mode\n"));
+ mch_msg("\n");
+ mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n"));
- mch_msg(_(" -v, --version Print version information and exit\n"));
- mch_msg(_(" -h, --help Print this help message and exit\n"));
+#if !defined(UNIX)
+ mch_msg(_(" --literal Don't expand wildcards\n"));
+#endif
+ mch_msg(_(" --noplugin Don't load plugins\n"));
+ mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n"));
+ mch_msg(_("\nSee \":help startup-options\" for all options.\n"));
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 5c6f4d0d07..6c873a96c0 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -58,8 +58,8 @@ static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */
static bool clipboard_didwarn_unnamed = false;
// for behavior between start_batch_changes() and end_batch_changes())
-static bool clipboard_delay_update = false; // delay clipboard update
static int batch_change_count = 0; // inside a script
+static bool clipboard_delay_update = false; // delay clipboard update
static bool clipboard_needs_update = false; // clipboard was updated
/*
@@ -5524,7 +5524,7 @@ int get_default_register_name(void)
}
/// Determine if register `*name` should be used as a clipboard.
-/// In an unnammed operation, `*name` is `NUL` and will be adjusted to `'*'/'+'` if
+/// In an unnamed operation, `*name` is `NUL` and will be adjusted to */+ if
/// `clipboard=unnamed[plus]` is set.
///
/// @param name The name of register, or `NUL` if unnamed.
@@ -5535,33 +5535,46 @@ int get_default_register_name(void)
/// if the register isn't a clipboard or provider isn't available.
static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
{
- if (*name == '*' || *name == '+') {
- if(!eval_has_provider("clipboard")) {
- if (!quiet) {
- EMSG("clipboard: No provider. Try \":CheckHealth\" or "
- "\":h clipboard\".");
- }
- return NULL;
- }
- return &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
- } else if ((*name == NUL) && (cb_flags & CB_UNNAMEDMASK)) {
- if(!eval_has_provider("clipboard")) {
- if (!quiet && !clipboard_didwarn_unnamed) {
- msg((char_u *)"clipboard: No provider. Try \":CheckHealth\" or "
- "\":h clipboard\".");
- clipboard_didwarn_unnamed = true;
- }
- return NULL;
+#define MSG_NO_CLIP "clipboard: No provider. " \
+ "Try \":CheckHealth\" or \":h clipboard\"."
+
+ yankreg_T *target = NULL;
+ bool explicit_cb_reg = (*name == '*' || *name == '+');
+ bool implicit_cb_reg = (*name == NUL) && (cb_flags & CB_UNNAMEDMASK);
+ int save_redir_off = redir_off;
+ if (!explicit_cb_reg && !implicit_cb_reg) {
+ goto end;
+ }
+
+ if (!eval_has_provider("clipboard")) {
+ if (batch_change_count == 1 && explicit_cb_reg && !quiet) {
+ redir_off = true; // Avoid recursion from :redir + emsg().
+ // Do NOT error (emsg()) here--if it interrupts :redir we get into
+ // a weird state, stuck in "redirect mode".
+ msg((char_u *)MSG_NO_CLIP);
+ } else if (batch_change_count == 1 && implicit_cb_reg
+ && !quiet && !clipboard_didwarn_unnamed) {
+ redir_off = true; // Avoid recursion from :redir + emsg().
+ msg((char_u *)MSG_NO_CLIP);
+ clipboard_didwarn_unnamed = true;
}
+ // ... else, be silent (don't flood during :while, :redir, etc.).
+ goto end;
+ }
+
+ if (explicit_cb_reg) {
+ target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
+ goto end;
+ } else { // unnamed register: "implicit" clipboard
if (writing && clipboard_delay_update) {
+ // For "set" (copy), defer the clipboard call.
clipboard_needs_update = true;
- return NULL;
+ goto end;
} else if (!writing && clipboard_needs_update) {
- // use the internal value
- return NULL;
+ // For "get" (paste), use the internal value.
+ goto end;
}
- yankreg_T *target;
if (cb_flags & CB_UNNAMEDPLUS) {
*name = (cb_flags & CB_UNNAMED && writing) ? '"': '+';
target = &y_regs[PLUS_REGISTER];
@@ -5569,10 +5582,12 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
*name = '*';
target = &y_regs[STAR_REGISTER];
}
- return target; // unnamed register
+ goto end;
}
- // don't do anything for other register names
- return NULL;
+
+end:
+ redir_off = save_redir_off;
+ return target;
}
static bool get_clipboard(int name, yankreg_T **target, bool quiet)
@@ -5740,7 +5755,7 @@ static void set_clipboard(int name, yankreg_T *reg)
(void)eval_call_provider("clipboard", "set", args);
}
-/// Avoid clipboard (slow) during batch operations (i.e., a script).
+/// Avoid slow things (clipboard) during batch operations (while/for-loops).
void start_batch_changes(void)
{
if (++batch_change_count > 1) {
@@ -5750,7 +5765,7 @@ void start_batch_changes(void)
clipboard_needs_update = false;
}
-/// Update the clipboard after batch changes finished.
+/// Counterpart to start_batch_changes().
void end_batch_changes(void)
{
if (--batch_change_count > 0) {
@@ -5759,6 +5774,7 @@ void end_batch_changes(void)
}
clipboard_delay_update = false;
if (clipboard_needs_update) {
+ // unnamed ("implicit" clipboard)
set_clipboard(NUL, y_previous);
clipboard_needs_update = false;
}
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index 64f7f31294..5a38178bd5 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -76,11 +76,11 @@ func Test_help_arg()
let found = []
for line in lines
if line =~ '-R.*Read-only mode'
- call add(found, 'Readonly mode')
+ call add(found, 'Readonly mode')
endif
" Watch out for a second --version line in the Gnome version.
- if line =~ '--version.*Print version information and exit'
- call add(found, "--version")
+ if line =~ '--version.*Print version information'
+ call add(found, "--version")
endif
endfor
call assert_equal(['Readonly mode', '--version'], found)
diff --git a/src/nvim/version.c b/src/nvim/version.c
index f5b45caefc..2a3fdbd70d 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -1089,13 +1089,7 @@ static void list_features(void)
msg_putchar('\n');
}
} else {
- while (msg_col % width) {
- int old_msg_col = msg_col;
- msg_putchar(' ');
- if (old_msg_col == msg_col) {
- break; // XXX: Avoid infinite loop.
- }
- }
+ msg_putchar(' ');
}
} else {
if (msg_col > 0) {
@@ -1103,7 +1097,7 @@ static void list_features(void)
}
}
}
- MSG_PUTS("For differences from Vim, see :help vim-differences\n\n");
+ MSG_PUTS("See \":help feature-compile\"\n\n");
}
void list_version(void)
@@ -1144,7 +1138,7 @@ void list_version(void)
}
#endif // ifdef HAVE_PATHDEF
- version_msg(_("\n\nOptional features included (+) or not (-): "));
+ version_msg(_("\n\nFeatures: "));
list_features();
@@ -1216,7 +1210,6 @@ void intro_message(int colon)
static char *(lines[]) = {
N_(NVIM_VERSION_LONG),
"",
- N_("by al."),
N_("Nvim is open source and freely distributable"),
N_("https://neovim.io/community"),
"",
diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/clipboard/clipboard_provider_spec.lua
index eb2eeee0da..e6544a8e2e 100644
--- a/test/functional/clipboard/clipboard_provider_spec.lua
+++ b/test/functional/clipboard/clipboard_provider_spec.lua
@@ -4,6 +4,8 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local feed_command, expect, eq, eval = helpers.feed_command, helpers.expect, helpers.eq, helpers.eval
+local command = helpers.command
+local meths = helpers.meths
local function basic_register_test(noblock)
insert("some words")
@@ -80,15 +82,47 @@ local function basic_register_test(noblock)
expect("two and three and one")
end
-describe('the unnamed register', function()
+describe('clipboard', function()
before_each(clear)
- it('works without provider', function()
+
+ it('unnamed register works without provider', function()
eq('"', eval('v:register'))
basic_register_test()
end)
+
+ it('`:redir @+>` with invalid g:clipboard shows error exactly once', function()
+ local screen = Screen.new(72, 5)
+ screen:attach()
+ command("let g:clipboard = 'bogus'")
+ feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ clipboard: No provider. Try ":CheckHealth" or ":h clipboard". |
+ ]], nil, {{bold = true, foreground = Screen.colors.Blue}})
+ end)
+
+ it('invalid g:clipboard', function()
+ command("let g:clipboard = 'bogus'")
+ eq('', eval('provider#clipboard#Executable()'))
+ eq('clipboard: invalid g:clipboard', eval('provider#clipboard#Error()'))
+ end)
+
+ it('valid g:clipboard', function()
+ -- provider#clipboard#Executable() only checks the structure.
+ meths.set_var('clipboard', {
+ ['name'] = 'clippy!',
+ ['copy'] = { ['+'] = 'any command', ['*'] = 'some other' },
+ ['paste'] = { ['+'] = 'any command', ['*'] = 'some other' },
+ })
+ eq('clippy!', eval('provider#clipboard#Executable()'))
+ eq('', eval('provider#clipboard#Error()'))
+ end)
end)
-describe('clipboard usage', function()
+describe('clipboard', function()
local function reset(...)
clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp', ...)
end
@@ -98,7 +132,23 @@ describe('clipboard usage', function()
feed_command('call getreg("*")') -- force load of provider
end)
- it('has independent "* and unnamed registers per default', function()
+ it('`:redir @+>` invokes clipboard once-per-message', function()
+ eq(0, eval("g:clip_called_set"))
+ feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END')
+ -- Assuming CONTRIBUTING.md has >100 lines.
+ assert(eval("g:clip_called_set") > 100)
+ end)
+
+ it('`:redir @">` does NOT invoke clipboard', function()
+ -- :redir to a non-clipboard register, with `:set clipboard=unnamed` does
+ -- NOT propagate to the clipboard. This is consistent with Vim.
+ command("set clipboard=unnamedplus")
+ eq(0, eval("g:clip_called_set"))
+ feed_command('redir @"> | :silent echo system("cat CONTRIBUTING.md") | redir END')
+ eq(0, eval("g:clip_called_set"))
+ end)
+
+ it('has independent "* and unnamed registers by default', function()
insert("some words")
feed('^"*dwdw"*P')
expect('some ')
@@ -139,7 +189,7 @@ describe('clipboard usage', function()
eq({'some\ntext', '\nvery binary\n'}, eval("getreg('*', 1, 1)"))
end)
- it('support autodectection of regtype', function()
+ it('autodetects regtype', function()
feed_command("let g:test_clip['*'] = ['linewise stuff','']")
feed_command("let g:test_clip['+'] = ['charwise','stuff']")
eq("V", eval("getregtype('*')"))
@@ -169,7 +219,7 @@ describe('clipboard usage', function()
eq({{' much', 'ktext', ''}, 'b'}, eval("g:test_clip['+']"))
end)
- it('supports setreg', function()
+ it('supports setreg()', function()
feed_command('call setreg("*", "setted\\ntext", "c")')
feed_command('call setreg("+", "explicitly\\nlines", "l")')
feed('"+P"*p')
@@ -187,7 +237,7 @@ describe('clipboard usage', function()
]])
end)
- it('supports let @+ (issue #1427)', function()
+ it('supports :let @+ (issue #1427)', function()
feed_command("let @+ = 'some'")
feed_command("let @* = ' other stuff'")
eq({{'some'}, 'v'}, eval("g:test_clip['+']"))
@@ -305,7 +355,7 @@ describe('clipboard usage', function()
end)
- describe('with clipboard=unnamedplus', function()
+ describe('clipboard=unnamedplus', function()
before_each(function()
feed_command('set clipboard=unnamedplus')
end)
@@ -349,6 +399,7 @@ describe('clipboard usage', function()
really unnamed
the plus]])
end)
+
it('is updated on global changes', function()
insert([[
text
diff --git a/test/functional/fixtures/autoload/provider/clipboard.vim b/test/functional/fixtures/autoload/provider/clipboard.vim
index 411e095c71..6d777255c8 100644
--- a/test/functional/fixtures/autoload/provider/clipboard.vim
+++ b/test/functional/fixtures/autoload/provider/clipboard.vim
@@ -5,7 +5,13 @@ let s:methods = {}
let g:cliplossy = 0
let g:cliperror = 0
+" Count how many times the clipboard was invoked.
+let g:clip_called_get = 0
+let g:clip_called_set = 0
+
function! s:methods.get(reg)
+ let g:clip_called_get += 1
+
if g:cliperror
return 0
end
@@ -19,6 +25,8 @@ function! s:methods.get(reg)
endfunction
function! s:methods.set(lines, regtype, reg)
+ let g:clip_called_set += 1
+
if a:reg == '"'
call s:methods.set(a:lines,a:regtype,'+')
call s:methods.set(a:lines,a:regtype,'*')