diff options
30 files changed, 509 insertions, 100 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f5a2987f01..4cafdef73f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,13 +290,14 @@ option(LOG_LIST_ACTIONS "Add list actions logging" OFF) add_definitions(-DINCLUDE_GENERATED_DECLARATIONS) -if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-undefined") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined") # For O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags on older systems # (pre POSIX.1-2008: glibc 2.11 and earlier). #4042 + # For ptsname(). #6743 add_definitions(-D_GNU_SOURCE) endif() diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 0b7cd5b2cd..df36969fe3 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -261,6 +261,8 @@ Name triggered by ~ |SwapExists| detected an existing swap file |TermOpen| when a terminal job starts |TermClose| when a terminal job ends +|ChanOpen| after a channel opened +|ChanInfo| after a channel has its state changed Options |FileType| when the 'filetype' option has been set @@ -487,6 +489,19 @@ BufWriteCmd Before writing the whole buffer to a file. *BufWritePost* BufWritePost After writing the whole buffer to a file (should undo the commands for BufWritePre). + *ChanInfo* +ChanInfo State of channel changed, for instance the + client of a RPC channel described itself. + Sets these |v:event| keys: + info + See |nvim_get_chan_info| for the format of the + info Dictionary. + *ChanOpen* +ChanOpen Just after a channel was opened. + Sets these |v:event| keys: + info + See |nvim_get_chan_info| for the format of the + info Dictionary. *CmdUndefined* CmdUndefined When a user command is used but it isn't defined. Useful for defining a command only diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt index b9dccc42a8..c6c827a748 100644 --- a/runtime/doc/diff.txt +++ b/runtime/doc/diff.txt @@ -165,7 +165,7 @@ loaded. Since Vim doesn't allow having two buffers for the same file, you need another buffer. This command is useful: > command DiffOrig vert new | set buftype=nofile | read ++edit # | 0d_ \ | diffthis | wincmd p | diffthis -(this is in |vimrc_example.vim|). Use ":DiffOrig" to see the differences +Use ":DiffOrig" to see the differences between the current buffer and the file it was loaded from. A buffer that is unloaded cannot be used for the diff. But it does work for diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 6c40264d86..e7fb632de8 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -655,9 +655,6 @@ Q Switch to "Ex" mode. This is a bit like typing ":" Vim will enter this mode by default if it's invoked as "ex" on the command-line. Use the ":vi" command |:visual| to exit "Ex" mode. - Note: In older versions of Vim "Q" formatted text, - that is now done with |gq|. But if you use the - |vimrc_example.vim| script "Q" works like "gq". *gQ* gQ Switch to "Ex" mode like with "Q", but really behave diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 600eb3ab9e..6f3a585ff3 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -761,9 +761,8 @@ g'{mark} g`{mark} Jump to the {mark}, but don't change the jumplist when jumping within the current buffer. Example: > g`" -< jumps to the last known position in a file. See - $VIMRUNTIME/vimrc_example.vim. - Also see |:keepjumps|. +< jumps to the last known position in a file. + See also |:keepjumps|. *:marks* :marks List all the current marks (not a motion command). diff --git a/runtime/doc/usr_01.txt b/runtime/doc/usr_01.txt index f3a5728d72..bc55e7cdce 100644 --- a/runtime/doc/usr_01.txt +++ b/runtime/doc/usr_01.txt @@ -57,12 +57,11 @@ make them visible with: > ============================================================================== *01.2* Vim installed *setup-vimrc_example* -It's not required for this tutorial, but we provide an example vimrc you may -use: +To create an empty vimrc: > - :!cp -i $VIMRUNTIME/vimrc_example.vim ~/.config/nvim/init.vim - -If the file already exists you probably want to keep it. + :call mkdir(stdpath('config'),'p') + :exe 'edit' stdpath('config').'/init.vim' + :write For more info see |vimrc|. diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index cb7bf94ddc..d1491e6b31 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -58,10 +58,9 @@ to write a Vim script file: |usr_41.txt|. ============================================================================== *05.2* The example vimrc file explained *vimrc_example.vim* -In the first chapter was explained how the example vimrc file can be used. -The file can be found here: +In the first chapter was explained how to create a vimrc file. > - $VIMRUNTIME/vimrc_example.vim ~ + :exe 'edit' stdpath('config').'/init.vim' In this section we will explain the various commands used in this file. This will give you hints about how to set up your own preferences. Not everything diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor index dce98d53a4..0d03103bca 100644 --- a/runtime/tutor/en/vim-01-beginner.tutor +++ b/runtime/tutor/en/vim-01-beginner.tutor @@ -906,16 +906,13 @@ You can find help on just about any subject, by giving an argument to the Vim has many more features than Vi, but most of them are disabled by default. To start using more features you have to create a "vimrc" file. - 1. Start editing the "vimrc" file. This depends on your system: - `:e ~/.config/nvim/init.vim`{vim} for Unix-like systems + 1. Start editing the "vimrc" file. + `:call mkdir(stdpath('config'),'p')`{vim} + `:exe 'edit' stdpath('config').'/init.vim'`{vim} - 2. Now read the example "vimrc" file contents: - `:r $VIMRUNTIME/vimrc_example.vim`{vim} - - 3. Write the file with: + 2. Write the file with: `:w`{vim} - The next time you start Vim it will use syntax highlighting. You can add all your preferred settings to this "vimrc" file. For more information type `:help vimrc-intro`{vim}. diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 17ee3ed711..692a0b51fd 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1030,6 +1030,16 @@ Array copy_array(Array array) return rv; } +Dictionary copy_dictionary(Dictionary dict) +{ + Dictionary rv = ARRAY_DICT_INIT; + for (size_t i = 0; i < dict.size; i++) { + KeyValuePair item = dict.items[i]; + PUT(rv, item.key.data, copy_object(item.value)); + } + return rv; +} + /// Creates a deep clone of an object Object copy_object(Object obj) { @@ -1047,12 +1057,7 @@ Object copy_object(Object obj) return ARRAY_OBJ(copy_array(obj.data.array)); case kObjectTypeDictionary: { - Dictionary rv = ARRAY_DICT_INIT; - for (size_t i = 0; i < obj.data.dictionary.size; i++) { - KeyValuePair item = obj.data.dictionary.items[i]; - PUT(rv, item.key.data, copy_object(item.value)); - } - return DICTIONARY_OBJ(rv); + return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary)); } default: abort(); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 7a951d4e67..f587948cf0 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -990,6 +990,118 @@ Array nvim_get_api_info(uint64_t channel_id) return rv; } +/// Identify the client for nvim. Can be called more than once, but subsequent +/// calls will remove earlier info, which should be resent if it is still +/// valid. (This could happen if a library first identifies the channel, and a +/// plugin using that library later overrides that info) +/// +/// @param name short name for the connected client +/// @param version Dictionary describing the version, with the following +/// possible keys (all optional) +/// - "major" major version (defaults to 0 if not set, for no release yet) +/// - "minor" minor version +/// - "patch" patch number +/// - "prerelease" string describing a prerelease, like "dev" or "beta1" +/// - "commit" hash or similar identifier of commit +/// @param type Must be one of the following values. A client library should +/// use "remote" if the library user hasn't specified other value. +/// - "remote" remote client that connected to nvim. +/// - "ui" gui frontend +/// - "embedder" application using nvim as a component, for instance +/// IDE/editor implementing a vim mode. +/// - "host" plugin host, typically started by nvim +/// - "plugin" single plugin, started by nvim +/// @param methods Builtin methods in the client. For a host, this does not +/// include plugin methods which will be discovered later. +/// The key should be the method name, the values are dicts with +/// the following (optional) keys: +/// - "async" if true, send as a notification. If false or unspecified, +/// use a blocking request +/// - "nargs" Number of arguments. Could be a single integer or an array +/// two integers, minimum and maximum inclusive. +/// Further keys might be added in later versions of nvim and unknown keys +/// are thus ignored. Clients must only use keys defined in this or later +/// versions of nvim! +/// +/// @param attributes Informal attributes describing the client. Clients might +/// define their own keys, but the following are suggested: +/// - "website" Website of client (for instance github repository) +/// - "license" Informal descripton of the license, such as "Apache 2", +/// "GPLv3" or "MIT" +/// - "logo" URI or path to image, preferably small logo or icon. +/// .png or .svg format is preferred. +/// +void nvim_set_client_info(uint64_t channel_id, String name, + Dictionary version, String type, + Dictionary methods, Dictionary attributes, + Error *err) + FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY +{ + Dictionary info = ARRAY_DICT_INIT; + PUT(info, "name", copy_object(STRING_OBJ(name))); + + version = copy_dictionary(version); + bool has_major = false; + for (size_t i = 0; i < version.size; i++) { + if (strequal(version.items[i].key.data, "major")) { + has_major = true; + break; + } + } + if (!has_major) { + PUT(version, "major", INTEGER_OBJ(0)); + } + PUT(info, "version", DICTIONARY_OBJ(version)); + + PUT(info, "type", copy_object(STRING_OBJ(type))); + PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods))); + PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes))); + + rpc_set_client_info(channel_id, info); +} + +/// Get information about a channel. +/// +/// @returns a Dictionary, describing a channel with the +/// following keys: +/// - "stream" the stream underlying the channel +/// - "stdio" stdin and stdout of this Nvim instance +/// - "stderr" stderr of this Nvim instance +/// - "socket" TCP/IP socket or named pipe +/// - "job" job with communication over its stdio +/// - "mode" how data received on the channel is interpreted +/// - "bytes" send and recieve raw bytes +/// - "terminal" a |terminal| instance interprets ASCII sequences +/// - "rpc" |RPC| communication on the channel is active +/// - "pty" Name of pseudoterminal, if one is used (optional). +/// On a POSIX system, this will be a device path like +/// /dev/pts/1. Even if the name is unknown, the key will +/// still be present to indicate a pty is used. This is +/// currently the case when using winpty on windows. +/// - "buffer" buffer with connected |terminal| instance (optional) +/// - "client" information about the client on the other end of the +/// RPC channel, if it has added it using +/// |nvim_set_client_info|. (optional) +/// +Dictionary nvim_get_chan_info(Integer chan, Error *err) + FUNC_API_SINCE(4) +{ + if (chan < 0) { + return (Dictionary)ARRAY_DICT_INIT; + } + return channel_info((uint64_t)chan); +} + +/// Get information about all open channels. +/// +/// @returns Array of Dictionaries, each describing a channel with +/// the format specified at |nvim_get_chan_info|. +Array nvim_list_chans(void) + FUNC_API_SINCE(4) +{ + return channel_all_info(); +} + /// Calls many API methods atomically. /// /// This has two main usages: diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index f07a92ab87..d0a3f38c6b 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -19,6 +19,8 @@ return { 'BufWriteCmd', -- write buffer using command 'BufWritePost', -- after writing a buffer 'BufWritePre', -- before writing a buffer + 'ChanOpen', -- channel was opened + 'ChanInfo', -- info was received about channel 'CmdLineEnter', -- after entering cmdline mode 'CmdLineLeave', -- before leaving cmdline mode 'CmdUndefined', -- command undefined diff --git a/src/nvim/channel.c b/src/nvim/channel.c index b37fa10b12..64d743891b 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -1,11 +1,13 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include "nvim/api/private/helpers.h" #include "nvim/api/ui.h" #include "nvim/channel.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/event/socket.h" +#include "nvim/fileio.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/os/shell.h" @@ -145,6 +147,9 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error) return false; } break; + + default: + abort(); } return true; @@ -180,47 +185,11 @@ static Channel *channel_alloc(ChannelStreamType type) return chan; } -/// Not implemented, only logging for now void channel_create_event(Channel *chan, const char *ext_source) { #if MIN_LOG_LEVEL <= INFO_LOG_LEVEL - const char *stream_desc; - const char *mode_desc; const char *source; - switch (chan->streamtype) { - case kChannelStreamProc: - if (chan->stream.proc.type == kProcessTypePty) { - stream_desc = "pty job"; - } else { - stream_desc = "job"; - } - break; - - case kChannelStreamStdio: - stream_desc = "stdio"; - break; - - case kChannelStreamSocket: - stream_desc = "socket"; - break; - - case kChannelStreamInternal: - stream_desc = "socket (internal)"; - break; - - default: - stream_desc = "?"; - } - - if (chan->is_rpc) { - mode_desc = ", rpc"; - } else if (chan->term) { - mode_desc = ", terminal"; - } else { - mode_desc = ""; - } - if (ext_source) { // TODO(bfredl): in a future improved traceback solution, // external events should be included. @@ -230,12 +199,21 @@ void channel_create_event(Channel *chan, const char *ext_source) source = (const char *)IObuff; } - ILOG("new channel %" PRIu64 " (%s%s): %s", chan->id, stream_desc, - mode_desc, source); + Dictionary info = channel_info(chan->id); + typval_T tv = TV_INITIAL_VALUE; + // TODO(bfredl): do the conversion in one step. Also would be nice + // to pretty print top level dict in defined order + (void)object_to_vim(DICTIONARY_OBJ(info), &tv, NULL); + char *str = encode_tv2json(&tv, NULL); + ILOG("new channel %" PRIu64 " (%s) : %s", chan->id, source, str); + xfree(str); + api_free_dictionary(info); + #else - (void)chan; (void)ext_source; #endif + + channel_info_changed(chan, true); } void channel_incref(Channel *chan) @@ -755,3 +733,95 @@ static void term_close(void *data) multiqueue_put(chan->events, term_delayed_free, 1, data); } +void channel_info_changed(Channel *chan, bool new) +{ + event_T event = new ? EVENT_CHANOPEN : EVENT_CHANINFO; + if (has_event(event)) { + channel_incref(chan); + multiqueue_put(main_loop.events, set_info_event, + 2, chan, event); + } +} + +static void set_info_event(void **argv) +{ + Channel *chan = argv[0]; + event_T event = (event_T)(ptrdiff_t)argv[1]; + + dict_T *dict = get_vim_var_dict(VV_EVENT); + Dictionary info = channel_info(chan->id); + typval_T retval; + (void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL); + tv_dict_add_dict(dict, S_LEN("info"), retval.vval.v_dict); + + apply_autocmds(event, NULL, NULL, false, curbuf); + + tv_dict_clear(dict); + api_free_dictionary(info); + channel_decref(chan); +} + +Dictionary channel_info(uint64_t id) +{ + Channel *chan = find_channel(id); + if (!chan) { + return (Dictionary)ARRAY_DICT_INIT; + } + + Dictionary info = ARRAY_DICT_INIT; + PUT(info, "id", INTEGER_OBJ((Integer)chan->id)); + + const char *stream_desc, *mode_desc; + switch (chan->streamtype) { + case kChannelStreamProc: + stream_desc = "job"; + if (chan->stream.proc.type == kProcessTypePty) { + const char *name = pty_process_tty_name(&chan->stream.pty); + PUT(info, "pty", STRING_OBJ(cstr_to_string(name))); + } + break; + + case kChannelStreamStdio: + stream_desc = "stdio"; + break; + + case kChannelStreamStderr: + stream_desc = "stderr"; + break; + + case kChannelStreamInternal: + PUT(info, "internal", BOOLEAN_OBJ(true)); + // FALLTHROUGH + + case kChannelStreamSocket: + stream_desc = "socket"; + break; + + default: + abort(); + } + PUT(info, "stream", STRING_OBJ(cstr_to_string(stream_desc))); + + if (chan->is_rpc) { + mode_desc = "rpc"; + PUT(info, "client", DICTIONARY_OBJ(rpc_client_info(chan))); + } else if (chan->term) { + mode_desc = "terminal"; + PUT(info, "buffer", BUFFER_OBJ(terminal_buf(chan->term))); + } else { + mode_desc = "bytes"; + } + PUT(info, "mode", STRING_OBJ(cstr_to_string(mode_desc))); + + return info; +} + +Array channel_all_info(void) +{ + Channel *channel; + Array ret = ARRAY_DICT_INIT; + map_foreach_value(channels, channel, { + ADD(ret, DICTIONARY_OBJ(channel_info(channel->id))); + }); + return ret; +} diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 7d5f80c531..4f70bcc41a 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1018,7 +1018,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he // needs a break here if (wp->w_p_lbr && vim_isbreak(c) - && !vim_isbreak(s[1]) + && !vim_isbreak((int)s[1]) && wp->w_p_wrap && (wp->w_width != 0)) { // Count all characters from first non-blank after a blank up to next @@ -1042,7 +1042,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he c = *s; if (!(c != NUL - && (vim_isbreak(c) || col2 == col || !vim_isbreak(*ps)))) { + && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) { break; } diff --git a/src/nvim/fold.c b/src/nvim/fold.c index ad9cd4d562..7a95f4ab0c 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2445,7 +2445,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, if (lvl < level) { /* End of fold found, update the length when it got shorter. */ if (fp->fd_len != flp->lnum - fp->fd_top) { - if (fp->fd_top + fp->fd_len > bot + 1) { + if (fp->fd_top + fp->fd_len - 1 > bot) { /* fold continued below bot */ if (getlevel == foldlevelMarker || getlevel == foldlevelExpr diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 32781cf4d9..26b84b7cc7 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -61,6 +61,7 @@ void rpc_start(Channel *channel) rpc->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); rpc->subscribed_events = pmap_new(cstr_t)(); rpc->next_request_id = 1; + rpc->info = (Dictionary)ARRAY_DICT_INIT; kv_init(rpc->call_stack); if (channel->streamtype != kChannelStreamInternal) { @@ -553,6 +554,7 @@ void rpc_free(Channel *channel) pmap_free(cstr_t)(channel->rpc.subscribed_events); kv_destroy(channel->rpc.call_stack); + api_free_dictionary(channel->rpc.info); } static bool is_rpc_response(msgpack_object *obj) @@ -642,6 +644,23 @@ static WBuffer *serialize_response(uint64_t channel_id, return rv; } +void rpc_set_client_info(uint64_t id, Dictionary info) +{ + Channel *chan = find_rpc_channel(id); + if (!chan) { + abort(); + } + + api_free_dictionary(chan->rpc.info); + chan->rpc.info = info; + channel_info_changed(chan, false); +} + +Dictionary rpc_client_info(Channel *chan) +{ + return copy_dictionary(chan->rpc.info); +} + #if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL #define REQ "[request] " #define RES "[response] " diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h index 6d8362e8b7..bfa7f7b87c 100644 --- a/src/nvim/msgpack_rpc/channel_defs.h +++ b/src/nvim/msgpack_rpc/channel_defs.h @@ -31,6 +31,7 @@ typedef struct { msgpack_unpacker *unpacker; uint64_t next_request_id; kvec_t(ChannelCallFrame *) call_stack; + Dictionary info; } RpcState; #endif // NVIM_MSGPACK_RPC_CHANNEL_DEFS_H diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index dfe2cfbb8d..71bb2d8a5e 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -115,6 +115,11 @@ error: return status; } +const char *pty_process_tty_name(PtyProcess *ptyproc) +{ + return ptsname(ptyproc->tty_fd); +} + void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height) FUNC_ATTR_NONNULL_ALL { diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index a6774a6275..c5f8efadff 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -183,6 +183,11 @@ cleanup: return status; } +const char *pty_process_tty_name(PtyProcess *ptyproc) +{ + return "?"; +} + void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/screen.c b/src/nvim/screen.c index f15afa619f..4299002084 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3454,7 +3454,8 @@ win_line ( } // Found last space before word: check for line break. - if (wp->w_p_lbr && c0 == c && vim_isbreak(c) && !vim_isbreak(*ptr)) { + if (wp->w_p_lbr && c0 == c && vim_isbreak(c) + && !vim_isbreak((int)(*ptr))) { int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0; char_u *p = ptr - (mb_off + 1); // TODO: is passing p for start of the line OK? diff --git a/src/nvim/tag.c b/src/nvim/tag.c index ba2727f0d7..d7bdf97c48 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2404,11 +2404,14 @@ jumpto_tag ( } } - /* If it was a CTRL-W CTRL-] command split window now. For ":tab tag" - * open a new tab page. */ + // If it was a CTRL-W CTRL-] command split window now. For ":tab tag" + // open a new tab page. if (postponed_split || cmdmod.tab != 0) { - (void)win_split(postponed_split > 0 ? postponed_split : 0, - postponed_split_flags); + if (win_split(postponed_split > 0 ? postponed_split : 0, + postponed_split_flags) == FAIL) { + RedrawingDisabled--; + goto erret; + } RESET_BINDING(curwin); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 31875fac31..39cb2b6372 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -619,6 +619,11 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, } } +Buffer terminal_buf(const Terminal *term) +{ + return term->buf_handle; +} + // }}} // libvterm callbacks {{{ diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index cd7a6cbc9c..c1ede08c31 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -89,7 +89,7 @@ NEW_TESTS ?= \ test_profile.res \ test_put.res \ test_quickfix.res \ - Test_quotestar.res \ + test_quotestar.res \ test_recover.res \ test_retab.res \ test_scrollbind.res \ diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 7c6d38d7ec..0c2ec08af3 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -9,8 +9,8 @@ func! Test_address_fold() call setline(1, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/', \ 'after fold 1', 'after fold 2', 'after fold 3']) setl fen fdm=marker - " The next ccommands should all copy the same part of the buffer, - " regardless of the adressing type, since the part to be copied + " The next commands should all copy the same part of the buffer, + " regardless of the addressing type, since the part to be copied " is folded away :1y call assert_equal(['int FuncName() {/*{{{*/', '1', '2', '3', '4', '5', '}/*}}}*/'], getreg(0,1,1)) @@ -381,3 +381,56 @@ func Test_foldopen_exception() endtry call assert_match('E492:', a) endfunc + +func Test_folddoopen_folddoclosed() + new + call setline(1, range(1, 9)) + set foldmethod=manual + 1,3 fold + 6,8 fold + + " Test without range. + folddoopen s/$/o/ + folddoclosed s/$/c/ + call assert_equal(['1c', '2c', '3c', + \ '4o', '5o', + \ '6c', '7c', '8c', + \ '9o'], getline(1, '$')) + + " Test with range. + call setline(1, range(1, 9)) + 1,8 folddoopen s/$/o/ + 4,$ folddoclosed s/$/c/ + call assert_equal(['1', '2', '3', + \ '4o', '5o', + \ '6c', '7c', '8c', + \ '9'], getline(1, '$')) + + set foldmethod& + bw! +endfunc + +func Test_fold_error() + new + call setline(1, [1, 2]) + + for fm in ['indent', 'expr', 'syntax', 'diff'] + exe 'set foldmethod=' . fm + call assert_fails('norm zf', 'E350:') + call assert_fails('norm zd', 'E351:') + call assert_fails('norm zE', 'E352:') + endfor + + set foldmethod=manual + call assert_fails('norm zd', 'E490:') + call assert_fails('norm zo', 'E490:') + call assert_fails('3fold', 'E16:') + + set foldmethod=marker + set nomodifiable + call assert_fails('1,2fold', 'E21:') + + set modifiable& + set foldmethod& + bw! +endfunc diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 71fa6632c7..1e910b6aa7 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -766,6 +766,115 @@ describe('api', function() end) end) + describe('nvim_list_chans and nvim_get_chan_info', function() + before_each(function() + command('autocmd ChanOpen * let g:opened_event = copy(v:event)') + command('autocmd ChanInfo * let g:info_event = copy(v:event)') + end) + local testinfo = { + stream = 'stdio', + id = 1, + mode = 'rpc', + client = {}, + } + local stderr = { + stream = 'stderr', + id = 2, + mode = 'bytes', + } + + it('returns {} for invalid channel', function() + eq({}, meths.get_chan_info(0)) + eq({}, meths.get_chan_info(-1)) + -- more preallocated numbers might be added, try something high + eq({}, meths.get_chan_info(10)) + end) + + it('works for stdio channel', function() + eq({[1]=testinfo,[2]=stderr}, meths.list_chans()) + eq(testinfo, meths.get_chan_info(1)) + eq(stderr, meths.get_chan_info(2)) + + meths.set_client_info("functionaltests", + {major=0, minor=3, patch=17}, + 'ui', + {do_stuff={n_args={2,3}}}, + {license= 'Apache2'}) + local info = { + stream = 'stdio', + id = 1, + mode = 'rpc', + client = { + name='functionaltests', + version={major=0, minor=3, patch=17}, + type='ui', + methods={do_stuff={n_args={2,3}}}, + attributes={license='Apache2'}, + }, + } + eq({info=info}, meths.get_var("info_event")) + eq({[1]=info, [2]=stderr}, meths.list_chans()) + eq(info, meths.get_chan_info(1)) + end) + + it('works for job channel', function() + if iswin() and os.getenv('APPVEYOR') ~= nil then + pending("jobstart(['cat']) unreliable on appveyor") + return + end + eq(3, eval("jobstart(['cat'], {'rpc': v:true})")) + local info = { + stream='job', + id=3, + mode='rpc', + client={}, + } + eq({info=info}, meths.get_var("opened_event")) + eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans()) + eq(info, meths.get_chan_info(3)) + eval('rpcrequest(3, "nvim_set_client_info", "cat", {}, "remote",'.. + '{"nvim_command":{"n_args":1}},'.. -- and so on + '{"description":"The Amazing Cat"})') + info = { + stream='job', + id=3, + mode='rpc', + client = { + name='cat', + version={major=0}, + type='remote', + methods={nvim_command={n_args=1}}, + attributes={description="The Amazing Cat"}, + }, + } + eq({info=info}, meths.get_var("info_event")) + eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans()) + end) + + it('works for :terminal channel', function() + command(":terminal") + eq({id=1}, meths.get_current_buf()) + eq(3, meths.buf_get_option(1, "channel")) + + local info = { + stream='job', + id=3, + mode='terminal', + buffer = 1, + pty='?', + } + local event = meths.get_var("opened_event") + if not iswin() then + info.pty = event.info.pty + neq(nil, string.match(info.pty, "^/dev/")) + end + eq({info=info}, event) + info.buffer = {id=1} + eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans()) + eq(info, meths.get_chan_info(3)) + end) + end) + describe('nvim_call_atomic', function() it('works', function() meths.buf_set_lines(0, 0, -1, true, {'first'}) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index bcf83698bb..3f54ff6f41 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -9,6 +9,7 @@ local command = helpers.command local feed_command = helpers.feed_command local funcs = helpers.funcs local meths = helpers.meths +local iswin = helpers.iswin local fname = 'Xtest-functional-ex_cmds-write' local fname_bak = fname .. '~' @@ -34,11 +35,14 @@ describe(':write', function() it('&backupcopy=auto preserves symlinks', function() command('set backupcopy=auto') write_file('test_bkc_file.txt', 'content0') - if helpers.iswin() then + if iswin() then command("silent !mklink test_bkc_link.txt test_bkc_file.txt") else command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") end + if eval('v:shell_error') ~= 0 then + pending('Cannot create symlink', function()end) + end source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -51,11 +55,14 @@ describe(':write', function() it('&backupcopy=no replaces symlink with new file', function() command('set backupcopy=no') write_file('test_bkc_file.txt', 'content0') - if helpers.iswin() then + if iswin() then command("silent !mklink test_bkc_link.txt test_bkc_file.txt") else command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") end + if eval('v:shell_error') ~= 0 then + pending('Cannot create symlink', function()end) + end source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -66,7 +73,8 @@ describe(':write', function() end) it("appends FIFO file", function() - if eval("executable('mkfifo')") == 0 then + -- mkfifo creates read-only .lnk files on Windows + if iswin() or eval("executable('mkfifo')") == 0 then pending('missing "mkfifo" command', function()end) return end @@ -88,7 +96,7 @@ describe(':write', function() command('let $HOME=""') eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~')) -- Message from check_overwrite - if not helpers.iswin() then + if not iswin() then eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), redir_exec('write .')) end @@ -108,7 +116,7 @@ describe(':write', function() funcs.setfperm(fname, 'r--------') eq('Vim(write):E505: "Xtest-functional-ex_cmds-write" is read-only (add ! to override)', exc_exec('write')) - if helpers.iswin() then + if iswin() then eq(0, os.execute('del /q/f ' .. fname)) eq(0, os.execute('rd /q/s ' .. fname_bak)) else @@ -117,7 +125,7 @@ describe(':write', function() end write_file(fname_bak, 'TTYX') -- FIXME: exc_exec('write!') outputs 0 in Windows - if helpers.iswin() then return end + if iswin() then return end lfs.link(fname_bak .. ('/xxxxx'):rep(20), fname, true) eq('Vim(write):E166: Can\'t open linked file for writing', exc_exec('write!')) diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua index eebbd70f2b..df0b9df5dd 100644 --- a/test/functional/ex_cmds/wviminfo_spec.lua +++ b/test/functional/ex_cmds/wviminfo_spec.lua @@ -3,21 +3,21 @@ local lfs = require('lfs') local command, eq, neq, spawn, nvim_prog, set_session, write_file = helpers.command, helpers.eq, helpers.neq, helpers.spawn, helpers.nvim_prog, helpers.set_session, helpers.write_file +local iswin = helpers.iswin +local read_file = helpers.read_file describe(':wshada', function() local shada_file = 'wshada_test' local session before_each(function() - if session then - session:close() - end - -- Override the default session because we need 'swapfile' for these tests. - session = spawn({nvim_prog, '-u', 'NONE', '-i', '/dev/null', '--embed', + session = spawn({nvim_prog, '-u', 'NONE', '-i', iswin() and 'nul' or '/dev/null', '--embed', '--cmd', 'set swapfile'}) set_session(session) - + end) + after_each(function () + session:close() os.remove(shada_file) end) @@ -36,7 +36,7 @@ describe(':wshada', function() write_file(shada_file, text) -- sanity check - eq(text, io.open(shada_file):read()) + eq(text, read_file(shada_file)) neq(nil, lfs.attributes(shada_file)) command('wsh! '..shada_file) @@ -49,8 +49,4 @@ describe(':wshada', function() assert(char1:byte() == 0x01, shada_file..' should be a shada file') end) - - teardown(function() - os.remove(shada_file) - end) end) diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index c2667d28d2..379646b2ba 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -17,9 +17,10 @@ local lfs = require('lfs') local clear, feed_command, expect, eq, neq, dedent, write_file, feed = helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.neq, helpers.dedent, helpers.write_file, helpers.feed +local iswin = helpers.iswin local function has_gzip() - local null = helpers.iswin() and 'nul' or '/dev/null' + local null = iswin() and 'nul' or '/dev/null' return os.execute('gzip --help >' .. null .. ' 2>&1') == 0 end @@ -59,7 +60,7 @@ describe('file reading, writing and bufnew and filter autocommands', function() os.remove('test.out') end) - if not has_gzip() then + if iswin() or not has_gzip() then pending('skipped (missing `gzip` utility)', function() end) else diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua index 907f0665ae..ccd93fed60 100644 --- a/test/functional/legacy/097_glob_path_spec.lua +++ b/test/functional/legacy/097_glob_path_spec.lua @@ -74,7 +74,7 @@ describe('glob() and globpath()', function() teardown(function() if helpers.iswin() then os.execute('del /q/f Xxx{ Xxx$') - os.execute('rd /q sautest') + os.execute('rd /q /s sautest') else os.execute("rm -rf sautest Xxx{ Xxx$") end diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index 5ef456bfe3..9ea3269828 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -4,6 +4,9 @@ local eq, eval, command = helpers.eq, helpers.eval, helpers.command describe('Test for delete()', function() before_each(clear) + after_each(function() + os.remove('Xfile') + end) it('file delete', function() command('split Xfile') @@ -52,6 +55,9 @@ describe('Test for delete()', function() silent !ln -s Xfile Xlink endif ]]) + if eval('v:shell_error') ~= 0 then + pending('Cannot create symlink', function()end) + end -- Delete the link, not the file eq(0, eval("delete('Xlink')")) eq(-1, eval("delete('Xlink')")) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 1fc045e0f7..eaeffa2449 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -222,7 +222,8 @@ if(WIN32) INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory . ${DEPS_INSTALL_DIR}/bin) GetBinaryDep(TARGET wingui - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory bin ${DEPS_INSTALL_DIR}/bin) + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory bin ${DEPS_INSTALL_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy_directory share ${DEPS_INSTALL_DIR}/share) include(TargetArch) GetBinaryDep(TARGET "win32yank_${TARGET_ARCH}" |