aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--README.md24
-rw-r--r--cmake/PreventInTreeBuilds.cmake23
-rw-r--r--runtime/autoload/health/provider.vim70
-rw-r--r--runtime/autoload/remote/host.vim1
-rw-r--r--runtime/doc/deprecated.txt1
-rw-r--r--runtime/doc/eval.txt53
-rw-r--r--runtime/optwin.vim8
-rw-r--r--runtime/vimrc_example.vim6
-rw-r--r--src/nvim/CMakeLists.txt2
-rw-r--r--src/nvim/buffer.c43
-rw-r--r--src/nvim/edit.c7
-rw-r--r--src/nvim/eval.c117
-rw-r--r--src/nvim/eval.lua3
-rw-r--r--src/nvim/ex_cmds.c8
-rw-r--r--src/nvim/ex_docmd.c10
-rw-r--r--src/nvim/main.c13
-rw-r--r--src/nvim/mbyte.c2
-rw-r--r--src/nvim/path.c33
-rw-r--r--src/nvim/terminal.c2
-rw-r--r--src/nvim/testdir/test_cmdline.vim44
-rw-r--r--src/nvim/testdir/test_window_id.vim15
-rw-r--r--src/nvim/tui/tui.c2
-rw-r--r--src/nvim/ui.c8
-rw-r--r--src/nvim/ui_bridge.c (renamed from src/nvim/tui/ui_bridge.c)7
-rw-r--r--src/nvim/ui_bridge.h (renamed from src/nvim/tui/ui_bridge.h)11
-rw-r--r--src/nvim/version.c16
-rw-r--r--src/nvim/vim.h3
-rw-r--r--src/nvim/window.c4
-rw-r--r--test/functional/api/server_requests_spec.lua3
-rw-r--r--test/functional/eval/timer_spec.lua68
-rw-r--r--test/functional/ex_cmds/arg_spec.lua30
-rw-r--r--test/functional/ex_cmds/edit_spec.lua26
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua26
-rw-r--r--test/functional/terminal/tui_spec.lua2
35 files changed, 500 insertions, 194 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index beca0c1d5d..4aa6a2de32 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,6 +8,9 @@ endif()
# Point CMake at any custom modules we may ship
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
+# We don't support building in-tree.
+include(PreventInTreeBuilds)
+
# Prefer our bundled versions of dependencies.
set(DEPS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/.deps/usr" CACHE PATH "Path prefix for finding dependencies")
if(CMAKE_CROSSCOMPILING AND NOT UNIX)
diff --git a/README.md b/README.md
index 03d478d42c..2d3d18a721 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,24 @@
-![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo.png)
+[![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo.png)](https://neovim.io)
-[Website] | [Community] | [Wiki] | [Documentation] | [Mailing List] | [Twitter] | [Bountysource]
+[Wiki](https://github.com/neovim/neovim/wiki) |
+[Documentation](https://neovim.io/doc) |
+[Twitter](https://twitter.com/Neovim) |
+[Community](https://neovim.io/community/) |
+[Gitter **Chat**](https://gitter.im/neovim/neovim)
[![Travis Build Status](https://travis-ci.org/neovim/neovim.svg?branch=master)](https://travis-ci.org/neovim/neovim)
[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/cf1jwc29198748we/branch/master?svg=true)](https://ci.appveyor.com/project/neovim/neovim/branch/master)
[![Pull requests waiting for review](https://badge.waffle.io/neovim/neovim.svg?label=RFC&title=RFCs)](https://waffle.io/neovim/neovim)
[![Coverage Status](https://img.shields.io/coveralls/neovim/neovim.svg)](https://coveralls.io/r/neovim/neovim)
[![Coverity Scan Build](https://scan.coverity.com/projects/2227/badge.svg)](https://scan.coverity.com/projects/2227)
-[![Clang Scan Build](http://neovim.io/doc/reports/clang/badge.svg)](http://neovim.io/doc/reports/clang)
-[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/neovim/neovim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Clang Scan Build](https://neovim.io/doc/reports/clang/badge.svg)](https://neovim.io/doc/reports/clang)
+<a href="https://buildd.debian.org/neovim"><img src="https://www.debian.org/logos/openlogo-nd-25.png" width="13" height="15">Debian</a>
Neovim is a project that seeks to aggressively refactor Vim in order to:
- Simplify maintenance and encourage [contributions](CONTRIBUTING.md)
- Split the work between multiple developers
-- Enable the implementation of new/modern UIs without modifications to the core
+- Enable advanced [external UIs] without modifications to the core
- Improve extensibility with a new [plugin architecture](https://github.com/neovim/neovim/wiki/Plugin-UI-architecture)
For more details, see
@@ -45,7 +49,7 @@ See the [Roadmap].
How do I get it?
----------------
-There is a formula for OSX/homebrew, a PKGBUILD for Arch Linux, RPM, deb, and
+There is a Debian package, homebrew formula, PKGBUILD for Arch Linux, RPM, and
more. See [the wiki](https://github.com/neovim/neovim/wiki/Installing-Neovim)!
License
@@ -74,15 +78,9 @@ See `LICENSE` for details.
You can also sponsor the development of Vim. Vim sponsors can vote for
features. The money goes to Uganda anyway.
-[Website]: http://neovim.io
-[Community]: http://neovim.io/community/
-[Wiki]: https://github.com/neovim/neovim/wiki
-[Documentation]: http://neovim.io/doc
-[Mailing List]: https://groups.google.com/forum/#!forum/neovim
-[Twitter]: http://twitter.com/Neovim
-[Bountysource]: https://www.bountysource.com/teams/neovim
[license-commit]: https://github.com/neovim/neovim/commit/b17d9691a24099c9210289f16afb1a498a89d803
[nvim-features]: https://neovim.io/doc/user/vim_diff.html#nvim-features
[Roadmap]: https://neovim.io/roadmap/
+[external UIs]: https://github.com/neovim/neovim/wiki/Related-projects#gui-projects
<!-- vim: set tw=80: -->
diff --git a/cmake/PreventInTreeBuilds.cmake b/cmake/PreventInTreeBuilds.cmake
new file mode 100644
index 0000000000..9c0ce1c0a2
--- /dev/null
+++ b/cmake/PreventInTreeBuilds.cmake
@@ -0,0 +1,23 @@
+function(PreventInTreeBuilds)
+ get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
+ get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
+
+ if("${srcdir}" STREQUAL "${bindir}")
+ message("")
+ message("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
+ message("Neovim doesn't support in-tree builds. It's recommended that you")
+ message("use a build/ subdirectory:")
+ message(" mkdir build")
+ message(" cd build")
+ message(" cmake <OPTIONS> ..")
+ message("")
+ message("Make sure to cleanup some CMake artifacts from this failed build")
+ message("with:")
+ message(" rm -rf CMakeFiles CMakeCache.txt")
+ message("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
+ message("")
+ message(FATAL_ERROR "Stopping build.")
+ endif()
+endfunction()
+
+PreventInTreeBuilds()
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index b1cfa8bf2b..d4b2f07a17 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -1,13 +1,5 @@
-let s:bad_responses = [
- \ 'unable to parse python response',
- \ 'unable to parse',
- \ 'unable to get pypi response',
- \ 'unable to get neovim executable',
- \ 'unable to find neovim version'
- \ ]
-
function! s:is_bad_response(s) abort
- return index(s:bad_responses, a:s) >= 0
+ return a:s =~? '\v(^unable)|(^error)'
endfunction
function! s:trim(s) abort
@@ -32,44 +24,41 @@ endfunction
" Fetch the contents of a URL.
function! s:download(url) abort
- let content = ''
if executable('curl')
- let content = system(['curl', '-sL', "'", a:url, "'"])
- endif
-
- if empty(content) && executable('python')
+ let rv = system(['curl', '-sL', a:url])
+ return v:shell_error ? 'curl error: '.v:shell_error : rv
+ elseif executable('python')
let script = "
\try:\n
\ from urllib.request import urlopen\n
\except ImportError:\n
\ from urllib2 import urlopen\n
\\n
- \try:\n
- \ response = urlopen('".a:url."')\n
- \ print(response.read().decode('utf8'))\n
- \except Exception:\n
- \ pass\n
+ \response = urlopen('".a:url."')\n
+ \print(response.read().decode('utf8'))\n
\"
- let content = system(['python', '-c', "'", script, "'", '2>/dev/null'])
+ let rv = system(['python', '-c', script])
+ return empty(rv) && v:shell_error
+ \ ? 'python urllib.request error: '.v:shell_error
+ \ : rv
endif
-
- return content
+ return 'missing `curl` and `python`, cannot make pypi request'
endfunction
-" Get the latest Neovim Python client version from PyPI. Result is cached.
+" Get the latest Neovim Python client version from PyPI.
function! s:latest_pypi_version() abort
- if exists('s:pypi_version')
- return s:pypi_version
- endif
-
- let s:pypi_version = 'unable to get pypi response'
- let pypi_info = s:download('https://pypi.python.org/pypi/neovim/json')
- if !empty(pypi_info)
- let pypi_data = json_decode(pypi_info)
- let s:pypi_version = get(get(pypi_data, 'info', {}), 'version', 'unable to parse')
- return s:pypi_version
+ let pypi_version = 'unable to get pypi response'
+ let pypi_response = s:download('https://pypi.python.org/pypi/neovim/json')
+ if !empty(pypi_response)
+ try
+ let pypi_data = json_decode(pypi_response)
+ catch /E474/
+ return 'error: '.pypi_response
+ endtry
+ let pypi_version = get(get(pypi_data, 'info', {}), 'version', 'unable to parse')
endif
+ return pypi_version
endfunction
" Get version information using the specified interpreter. The interpreter is
@@ -97,11 +86,11 @@ function! s:version_info(python) abort
let nvim_path = s:trim(system([
\ a:python,
\ '-c',
- \ 'import neovim; print(neovim.__file__)',
- \ '2>/dev/null']))
+ \ 'import neovim; print(neovim.__file__)']))
+ let nvim_path = v:shell_error ? '' : nvim_path
if empty(nvim_path)
- return [python_version, 'unable to find neovim executable', pypi_version, 'unable to get neovim executable']
+ return [python_version, 'unable to find nvim executable', pypi_version, 'unable to get nvim executable']
endif
" Assuming that multiple versions of a package are installed, sort them
@@ -112,7 +101,7 @@ function! s:version_info(python) abort
return a == b ? 0 : a > b ? 1 : -1
endfunction
- let nvim_version = 'unable to find neovim version'
+ let nvim_version = 'unable to find nvim version'
let base = fnamemodify(nvim_path, ':h')
let metas = glob(base.'-*/METADATA', 1, 1) + glob(base.'-*/PKG-INFO', 1, 1)
let metas = sort(metas, 's:compare')
@@ -334,12 +323,13 @@ function! s:check_python(version) abort
endif
if s:is_bad_response(latest)
- call health#report_warn('Unable to fetch latest Neovim Python client version.')
+ call health#report_warn('Unable to contact PyPI.')
+ call health#report_error('HTTP request failed: '.latest)
endif
if s:is_bad_response(status)
- call health#report_warn('Latest Neovim Python client versions: ('.latest.')')
- else
+ call health#report_warn('Latest Neovim Python client version: ('.latest.')')
+ elseif !s:is_bad_response(latest)
call health#report_ok('Latest Neovim Python client is installed: ('.status.')')
endif
endif
diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim
index d4e8e98bc0..1f30b91ab8 100644
--- a/runtime/autoload/remote/host.vim
+++ b/runtime/autoload/remote/host.vim
@@ -191,6 +191,7 @@ function! s:RegistrationCommands(host) abort
let pattern = s:plugin_patterns[a:host]
let paths = globpath(&rtp, 'rplugin/'.a:host.'/'.pattern, 0, 1)
let paths = map(paths, 'tr(v:val,"\\","/")') " Normalize slashes #4795
+ let paths = uniq(sort(paths))
if empty(paths)
return []
endif
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index d108aca62f..2b69929cfe 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -41,3 +41,4 @@ Options ~
*'vi'*
*'viminfo'* Deprecated alias to 'shada' option.
+ vim:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index df5713c63d..dedbe49605 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1813,6 +1813,7 @@ buflisted({expr}) Number TRUE if buffer {expr} is listed
bufloaded({expr}) Number TRUE if buffer {expr} is loaded
bufname({expr}) String Name of the buffer {expr}
bufnr({expr} [, {create}]) Number Number of the buffer {expr}
+bufwinid({expr}) Number window ID of buffer {expr}
bufwinnr({expr}) Number window number of buffer {expr}
byte2line({byte}) Number line number at byte count {byte}
byteidx({expr}, {nr}) Number byte index of {nr}'th char in {expr}
@@ -1897,7 +1898,8 @@ getcmdline() String return the current command-line
getcmdpos() Number return cursor position in command-line
getcmdtype() String return current command-line type
getcmdwintype() String return current command-line window type
-getcompletion({pat}, {type}) List list of cmdline completion matches
+getcompletion({pat}, {type} [, {filtered}])
+ List list of cmdline completion matches
getcurpos() List position of the cursor
getcwd([{winnr} [, {tabnr}]]) String the current working directory
getfontname([{name}]) String name of font being used
@@ -2243,6 +2245,7 @@ arglistid([{winnr} [, {tabnr}]])
With {winnr} only use this window in the current tab page.
With {winnr} and {tabnr} use the window in the specified tab
page.
+ {winnr} can be the window number or the window ID.
*argv()*
argv([{nr}]) The result is the {nr}th file in the argument list of the
@@ -2468,6 +2471,16 @@ bufnr({expr} [, {create}])
number necessarily exist, because ":bwipeout" may have removed
them. Use bufexists() to test for the existence of a buffer.
+bufwinid({expr}) *bufwinid()*
+ The result is a Number, which is the window ID of the first
+ window associated with buffer {expr}. For the use of {expr},
+ see |bufname()| above. If buffer {expr} doesn't exist or
+ there is no such window, -1 is returned. Example: >
+
+ echo "A window containing buffer 1 is " . (bufwinid(1))
+<
+ Only deals with the current tab page.
+
bufwinnr({expr}) *bufwinnr()*
The result is a Number, which is the number of the first
window associated with buffer {expr}. For the use of {expr},
@@ -3651,7 +3664,7 @@ getcmdwintype() *getcmdwintype()*
values are the same as |getcmdtype()|. Returns an empty string
when not in the command-line window.
-getcompletion({pat}, {type}) *getcompletion()*
+getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
Return a list of command-line completion matches. {type}
specifies what for. The following completion types are
supported:
@@ -3691,6 +3704,10 @@ getcompletion({pat}, {type}) *getcompletion()*
Otherwise only items matching {pat} are returned. See
|wildcards| for the use of special characters in {pat}.
+ If the optional {filtered} flag is set to 1, then 'wildignore'
+ is applied to filter the results. Otherwise all the matches
+ are returned. The 'wildignorecase' option always applies.
+
If there are no matches, an empty list is returned. An
invalid value for {type} produces an error.
@@ -3716,6 +3733,7 @@ getcwd([{winnr}[, {tabnr}]]) *getcwd()*
getcwd(0)
getcwd(0, 0)
< If {winnr} is -1 it is ignored, only the tab is resolved.
+ {winnr} can be the window number or the window ID.
getfsize({fname}) *getfsize()*
@@ -3810,7 +3828,9 @@ getline({lnum} [, {end}])
getloclist({nr}) *getloclist()*
Returns a list with all the entries in the location list for
- window {nr}. When {nr} is zero the current window is used.
+ window {nr}. {nr} can be the window number or the window ID.
+ When {nr} is zero the current window is used.
+
For a location list window, the displayed location list is
returned. For an invalid window number {nr}, an empty list is
returned. Otherwise, same as |getqflist()|.
@@ -3938,6 +3958,7 @@ gettabwinvar({tabnr}, {winnr}, {varname} [, {def}]) *gettabwinvar()*
Note that {varname} must be the name without "w:".
Tabs are numbered starting with one. For the current tabpage
use |getwinvar()|.
+ {winnr} can be the window number or the window ID.
When {winnr} is zero the current window is used.
This also works for a global option, buffer-local option and
window-local option, but it doesn't work for a global variable
@@ -4065,7 +4086,8 @@ haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()*
haslocaldir()
haslocaldir(0)
haslocaldir(0, 0)
-< If {winnr} is -1 it is ignored, only the tab is resolved.
+< {winnr} can be the window number or the window ID.
+ If {winnr} is -1 it is ignored, only the tab is resolved.
hasmapto({what} [, {mode} [, {abbr}]]) *hasmapto()*
The result is a Number, which is 1 if there is a mapping that
@@ -6054,11 +6076,13 @@ setline({lnum}, {text}) *setline()*
setloclist({nr}, {list} [, {action}[, {title}]]) *setloclist()*
Create or replace or add to the location list for window {nr}.
- When {nr} is zero the current window is used. For a location
- list window, the displayed location list is modified. For an
- invalid window number {nr}, -1 is returned. If {title} is
- given, it will be used to set |w:quickfix_title| after opening
- the location window.
+ {nr} can be the window number or the window ID.
+ When {nr} is zero the current window is used.
+
+ For a location list window, the displayed location list is
+ modified. For an invalid window number {nr}, -1 is returned. If
+ {title} is given, it will be used to set |w:quickfix_title|
+ after opening the location window.
Otherwise, same as |setqflist()|.
Also see |location-list|.
@@ -6222,6 +6246,7 @@ settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()*
{val}.
Tabs are numbered starting with one. For the current tabpage
use |setwinvar()|.
+ {winnr} can be the window number or the window ID.
When {winnr} is zero the current window is used.
This also works for a global or local buffer option, but it
doesn't work for a global or local buffer variable.
@@ -7250,9 +7275,11 @@ win_id2win({expr}) *win_id2win()*
*winbufnr()*
winbufnr({nr}) The result is a Number, which is the number of the buffer
- associated with window {nr}. When {nr} is zero, the number of
- the buffer in the current window is returned. When window
- {nr} doesn't exist, -1 is returned.
+ associated with window {nr}. {nr} can be the window number or
+ the window ID.
+ When {nr} is zero, the number of the buffer in the current
+ window is returned.
+ When window {nr} doesn't exist, -1 is returned.
Example: >
:echo "The file in the current window is " . bufname(winbufnr(0))
<
@@ -7263,6 +7290,7 @@ wincol() The result is a Number, which is the virtual column of the
winheight({nr}) *winheight()*
The result is a Number, which is the height of window {nr}.
+ {nr} can be the window number or the window ID.
When {nr} is zero, the height of the current window is
returned. When window {nr} doesn't exist, -1 is returned.
An existing window always has a height of zero or more.
@@ -7342,6 +7370,7 @@ winsaveview() Returns a |Dictionary| that contains information to restore
winwidth({nr}) *winwidth()*
The result is a Number, which is the width of window {nr}.
+ {nr} can be the window number or the window ID.
When {nr} is zero, the width of the current window is
returned. When window {nr} doesn't exist, -1 is returned.
An existing window always has a width of zero or more.
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 5fcfad04a3..3d585267d7 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -710,6 +710,10 @@ call <SID>Header("editing text")
call append("$", "undolevels\tmaximum number of changes that can be undone")
call append("$", "\t(global or local to buffer)")
call append("$", " \tset ul=" . &ul)
+call append("$", "undofile\tautomatically save and restore undo history")
+call <SID>BinOptionG("udf", &udf)
+call append("$", "undodir\tlist of directories for undo files")
+call <SID>OptionG("udir", &udir)
call append("$", "undoreload\tmaximum number lines to save for undo on a buffer reload")
call append("$", " \tset ur=" . &ur)
call append("$", "modified\tchanges have been made and not written to a file")
@@ -1032,10 +1036,6 @@ if has("vertsplit")
call append("$", "cmdwinheight\theight of the command-line window")
call <SID>OptionG("cwh", &cwh)
endif
-call append("$", "undofile\tautomatically save and restore undo history")
-call <SID>BinOptionG("udf", &udf)
-call append("$", "undodir\tlist of directories for undo files")
-call <SID>OptionG("udir", &udir)
call <SID>Header("executing external commands")
diff --git a/runtime/vimrc_example.vim b/runtime/vimrc_example.vim
index c53dde8ceb..17cba123a8 100644
--- a/runtime/vimrc_example.vim
+++ b/runtime/vimrc_example.vim
@@ -19,6 +19,12 @@ inoremap <C-U> <C-G>u<C-U>
" Switch syntax highlighting on
syntax on
+" Also switch on highlighting the last used search pattern.
+set hlsearch
+
+" I like highlighting strings inside C comments.
+let c_comment_strings=1
+
" Enable file type detection.
" Use the default filetype settings, so that mail gets 'textwidth' set to 72,
" 'cindent' is on in C files, etc.
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 59582d0734..cbea6a05c9 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -156,7 +156,7 @@ foreach(sfile ${NEOVIM_SOURCES}
${GENERATED_API_DISPATCH})
get_filename_component(full_d ${sfile} PATH)
file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}")
- if(${d} MATCHES "^[.][.]")
+ if(${d} MATCHES "^([.][.]|auto/)")
file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}")
endif()
get_filename_component(f ${sfile} NAME)
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index b42ad1c18a..5fb011885e 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -276,30 +276,25 @@ bool buf_valid(buf_T *buf)
return false;
}
-/*
- * Close the link to a buffer.
- * "action" is used when there is no longer a window for the buffer.
- * It can be:
- * 0 buffer becomes hidden
- * DOBUF_UNLOAD buffer is unloaded
- * DOBUF_DELETE buffer is unloaded and removed from buffer list
- * DOBUF_WIPE buffer is unloaded and really deleted
- * When doing all but the first one on the current buffer, the caller should
- * get a new buffer very soon!
- *
- * The 'bufhidden' option can force freeing and deleting.
- *
- * When "abort_if_last" is TRUE then do not close the buffer if autocommands
- * cause there to be only one window with this buffer. e.g. when ":quit" is
- * supposed to close the window but autocommands close all other windows.
- */
-void
-close_buffer (
- win_T *win, /* if not NULL, set b_last_cursor */
- buf_T *buf,
- int action,
- int abort_if_last
-)
+/// Close the link to a buffer.
+///
+/// @param win If not NULL, set b_last_cursor.
+/// @param buf
+/// @param action Used when there is no longer a window for the buffer.
+/// Possible values:
+/// 0 buffer becomes hidden
+/// DOBUF_UNLOAD buffer is unloaded
+/// DOBUF_DELETE buffer is unloaded and removed from buffer list
+/// DOBUF_WIPE buffer is unloaded and really deleted
+/// When doing all but the first one on the current buffer, the
+/// caller should get a new buffer very soon!
+/// The 'bufhidden' option can force freeing and deleting.
+/// @param abort_if_last
+/// If TRUE, do not close the buffer if autocommands cause
+/// there to be only one window with this buffer. e.g. when
+/// ":quit" is supposed to close the window but autocommands
+/// close all other windows.
+void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
{
bool unload_buf = (action != 0);
bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 892748ff5c..51c9fb1556 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1286,10 +1286,9 @@ bool edit(int cmdchar, bool startln, long count)
{
if (curbuf->terminal) {
if (ex_normal_busy) {
- // don't enter terminal mode from `ex_normal`, which can result in all
- // kinds of havoc(such as terminal mode recursiveness). Instead, set a
- // flag that allow us to force-set the value of `restart_edit` before
- // `ex_normal` returns
+ // Do not enter terminal mode from ex_normal(), which would cause havoc
+ // (such as terminal-mode recursiveness). Instead set a flag to force-set
+ // the value of `restart_edit` before `ex_normal` returns.
restart_edit = 'i';
force_restart_edit = true;
} else {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 0b1fd6670e..dce2f32707 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -405,7 +405,7 @@ typedef struct {
LibuvProcess uv;
PtyProcess pty;
} proc;
- Stream in, out, err;
+ Stream in, out, err; // Initialized in common_job_start().
Terminal *term;
bool stopped;
bool exited;
@@ -6709,7 +6709,6 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
# include "funcs.generated.h"
#endif
-
/*
* Function given to ExpandGeneric() to obtain the list of internal
* or user defined function names.
@@ -7712,26 +7711,47 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = -1;
}
-/*
- * "bufwinnr(nr)" function
- */
-static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr)
{
- (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
- ++emsg_off;
+ int error = false;
+ (void)get_tv_number_chk(&argvars[0], &error); // issue errmsg if type error
+ if (error) { // the argument has an invalid type
+ rettv->vval.v_number = -1;
+ return;
+ }
+
+ emsg_off++;
+ buf_T *buf = get_buf_tv(&argvars[0], true);
+ if (buf == NULL) { // no need to search if buffer was not found
+ rettv->vval.v_number = -1;
+ goto end;
+ }
- buf_T *buf = get_buf_tv(&argvars[0], TRUE);
int winnr = 0;
+ int winid;
bool found_buf = false;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- ++winnr;
+ winnr++;
if (wp->w_buffer == buf) {
found_buf = true;
+ winid = wp->handle;
break;
}
}
- rettv->vval.v_number = (found_buf ? winnr : -1);
- --emsg_off;
+ rettv->vval.v_number = (found_buf ? (get_nr ? winnr : winid) : -1);
+end:
+ emsg_off--;
+}
+
+/// "bufwinid(nr)" function
+static void f_bufwinid(typval_T *argvars, typval_T *rettv, FunPtr fptr) {
+ buf_win_common(argvars, rettv, false);
+}
+
+/// "bufwinnr(nr)" function
+static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ buf_win_common(argvars, rettv, true);
}
/*
@@ -9507,24 +9527,35 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
varnumber_T n;
int error = FALSE;
- /* Position the cursor. Needed after a message that ends in a space. */
- ui_cursor_goto(msg_row, msg_col);
-
++no_mapping;
++allow_keys;
for (;; ) {
- if (argvars[0].v_type == VAR_UNKNOWN)
- /* getchar(): blocking wait. */
+ // Position the cursor. Needed after a message that ends in a space,
+ // or if event processing caused a redraw.
+ ui_cursor_goto(msg_row, msg_col);
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ // getchar(): blocking wait.
+ if (!(char_avail() || using_script() || input_available())) {
+ input_enable_events();
+ (void)os_inchar(NULL, 0, -1, 0);
+ input_disable_events();
+ if (!multiqueue_empty(main_loop.events)) {
+ multiqueue_process_events(main_loop.events);
+ continue;
+ }
+ }
n = safe_vgetc();
- else if (get_tv_number_chk(&argvars[0], &error) == 1)
- /* getchar(1): only check if char avail */
+ } else if (get_tv_number_chk(&argvars[0], &error) == 1) {
+ // getchar(1): only check if char avail
n = vpeekc_any();
- else if (error || vpeekc_any() == NUL)
- /* illegal argument or getchar(0) and no char avail: return zero */
+ } else if (error || vpeekc_any() == NUL) {
+ // illegal argument or getchar(0) and no char avail: return zero
n = 0;
- else
- /* getchar(0) and char avail: return char */
+ } else {
+ // getchar(0) and char avail: return char
n = safe_vgetc();
+ }
if (n == K_IGNORE)
continue;
@@ -9648,13 +9679,23 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *pat;
expand_T xpc;
+ bool filtered = false;
int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
| WILD_NO_BEEP;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ filtered = get_tv_number_chk(&argvars[2], NULL);
+ }
+
if (p_wic) {
options |= WILD_ICASE;
}
+ // For filtered results, 'wildignore' is used
+ if (!filtered) {
+ options |= WILD_KEEP_ALL;
+ }
+
ExpandInit(&xpc);
xpc.xp_pattern = get_tv_string(&argvars[0]);
xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
@@ -9673,6 +9714,16 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
}
+ if (xpc.xp_context == EXPAND_CSCOPE) {
+ set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope);
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ }
+
+ if (xpc.xp_context == EXPAND_SIGN) {
+ set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ }
+
pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
rettv_list_alloc(rettv);
if (pat != NULL) {
@@ -10259,7 +10310,11 @@ find_win_by_nr (
}
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
- if (--nr <= 0) {
+ if (nr >= LOWEST_WIN_ID) {
+ if (wp->handle == nr) {
+ return wp;
+ }
+ } else if (--nr <= 0) {
return wp;
}
}
@@ -21739,7 +21794,7 @@ static inline TerminalJobData *common_job_init(char **argv,
if (!pty) {
proc->err = &data->err;
}
- proc->cb = on_process_exit;
+ proc->cb = eval_job_process_exit_cb;
proc->events = data->events;
proc->detach = detach;
proc->cwd = cwd;
@@ -21923,7 +21978,7 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf,
rbuffer_consumed(buf, count);
}
-static void on_process_exit(Process *proc, int status, void *d)
+static void eval_job_process_exit_cb(Process *proc, int status, void *d)
{
TerminalJobData *data = d;
if (data->term && !data->exited) {
@@ -21947,9 +22002,15 @@ static void on_process_exit(Process *proc, int status, void *d)
static void term_write(char *buf, size_t size, void *d)
{
- TerminalJobData *data = d;
+ TerminalJobData *job = d;
+ if (job->in.closed) {
+ // If the backing stream was closed abruptly, there may be write events
+ // ahead of the terminal close event. Just ignore the writes.
+ ILOG("write failed: stream is closed");
+ return;
+ }
WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree);
- wstream_write(&data->in, wbuf);
+ wstream_write(&job->in, wbuf);
}
static void term_resize(uint16_t width, uint16_t height, void *d)
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 3c371fc264..eaaee81533 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -45,6 +45,7 @@ return {
bufloaded={args=1},
bufname={args=1},
bufnr={args={1, 2}},
+ bufwinid={args=1},
bufwinnr={args=1},
byte2line={args=1},
byteidx={args=2},
@@ -114,7 +115,7 @@ return {
getcmdpos={},
getcmdtype={},
getcmdwintype={},
- getcompletion={args=2},
+ getcompletion={args={2, 3}},
getcurpos={},
getcwd={args={0,2}},
getfontname={args={0, 1}},
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 0226499e78..6205daf0cb 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2116,6 +2116,14 @@ do_ecmd (
}
}
+ // Re-editing a terminal buffer: skip most buffer re-initialization.
+ if (!other_file && curbuf->terminal) {
+ check_arg_idx(curwin); // Needed when called from do_argfile().
+ maketitle(); // Title may show the arg index, e.g. "(2 of 5)".
+ retval = OK;
+ goto theend;
+ }
+
/*
* if the file was changed we may not be allowed to abandon it
* - if we are going to re-edit the same file
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 45407b7f12..9f83688e1e 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -6726,11 +6726,6 @@ do_exedit (
old_curwin == NULL ? curwin : NULL);
} else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit)
|| *eap->arg != NUL) {
- // ":edit <blank>" is a no-op in terminal buffers. #2822
- if (curbuf->terminal != NULL && eap->cmdidx == CMD_edit && *eap->arg == NUL) {
- return;
- }
-
/* Can't edit another file when "curbuf_lock" is set. Only ":edit"
* can bring us here, others are stopped earlier. */
if (*eap->arg != NUL && curbuf_locked())
@@ -7921,9 +7916,8 @@ static void ex_normal(exarg_T *eap)
if (force_restart_edit) {
force_restart_edit = false;
} else {
- // some function called was aware of ex_normal and decided to override the
- // value of restart_edit anyway. So far only used in terminal mode(see
- // terminal_enter() in edit.c)
+ // Some function (terminal_enter()) was aware of ex_normal and decided to
+ // override the value of restart_edit anyway.
restart_edit = save_restart_edit;
}
p_im = save_insertmode;
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 005f4dcc77..eb67483d08 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -270,11 +270,6 @@ int main(int argc, char **argv)
setbuf(stdout, NULL);
- /* This message comes before term inits, but after setting "silent_mode"
- * when the input is not a tty. */
- if (GARGCOUNT > 1 && !silent_mode)
- printf(_("%d files to edit\n"), GARGCOUNT);
-
full_screen = true;
check_tty(&params);
@@ -320,14 +315,18 @@ int main(int argc, char **argv)
// open terminals when opening files that start with term://
#define PROTO "term://"
+ do_cmdline_cmd("augroup nvim_terminal");
+ do_cmdline_cmd("autocmd!");
do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested "
- ":call termopen( "
+ ":if !exists('b:term_title')|call termopen( "
// Capture the command string
"matchstr(expand(\"<amatch>\"), "
"'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
// capture the working directory
"{'cwd': get(matchlist(expand(\"<amatch>\"), "
- "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})");
+ "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})"
+ "|endif");
+ do_cmdline_cmd("augroup END");
#undef PROTO
/* Execute --cmd arguments. */
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index c08b9e8fcf..e6312f9c00 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1724,7 +1724,7 @@ int utf_class(int c)
return 2;
}
-int utf_ambiguous_width(int c)
+bool utf_ambiguous_width(int c)
{
return c >= 0x80 && (intable(ambiguous, ARRAY_SIZE(ambiguous), c)
|| intable(emoji_all, ARRAY_SIZE(emoji_all), c));
diff --git a/src/nvim/path.c b/src/nvim/path.c
index a79b7139f1..6149e1ab99 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -903,17 +903,30 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
/* Shorten the filename while maintaining its uniqueness */
path_cutoff = get_path_cutoff(path, &path_ga);
- /* we start at the end of the path */
- pathsep_p = path + len - 1;
-
- while (find_previous_pathsep(path, &pathsep_p))
- if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
- && is_unique(pathsep_p + 1, gap, i)
- && path_cutoff != NULL && pathsep_p + 1 >= path_cutoff) {
- sort_again = true;
- memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
- break;
+ // Don't assume all files can be reached without path when search
+ // pattern starts with **/, so only remove path_cutoff
+ // when possible.
+ if (pattern[0] == '*' && pattern[1] == '*'
+ && vim_ispathsep_nocolon(pattern[2])
+ && path_cutoff != NULL
+ && vim_regexec(&regmatch, path_cutoff, (colnr_T)0)
+ && is_unique(path_cutoff, gap, i)) {
+ sort_again = true;
+ memmove(path, path_cutoff, STRLEN(path_cutoff) + 1);
+ } else {
+ // Here all files can be reached without path, so get shortest
+ // unique path. We start at the end of the path. */
+ pathsep_p = path + len - 1;
+ while (find_previous_pathsep(path, &pathsep_p)) {
+ if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
+ && is_unique(pathsep_p + 1, gap, i)
+ && path_cutoff != NULL && pathsep_p + 1 >= path_cutoff) {
+ sort_again = true;
+ memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
+ break;
+ }
}
+ }
if (path_is_absolute_path(path)) {
/*
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 8401343d7a..499716a7a8 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -366,10 +366,10 @@ void terminal_resize(Terminal *term, uint16_t width, uint16_t height)
void terminal_enter(void)
{
buf_T *buf = curbuf;
+ assert(buf->terminal); // Should only be called when curbuf has a terminal.
TerminalState state, *s = &state;
memset(s, 0, sizeof(TerminalState));
s->term = buf->terminal;
- assert(s->term && "should only be called when curbuf has a terminal");
// Ensure the terminal is properly sized.
terminal_resize(s->term, 0, 0);
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 902ec1c05d..21bb057fe1 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -89,6 +89,10 @@ func Test_getcompletion()
call assert_true(index(l, 'runtest.vim') >= 0)
let l = getcompletion('walk', 'file')
call assert_equal([], l)
+ set wildignore=*.vim
+ let l = getcompletion('run', 'file', 1)
+ call assert_true(index(l, 'runtest.vim') < 0)
+ set wildignore&
let l = getcompletion('ha', 'filetype')
call assert_true(index(l, 'hamster') >= 0)
@@ -121,12 +125,35 @@ func Test_getcompletion()
let l = getcompletion('dark', 'highlight')
call assert_equal([], l)
+ if has('cscope')
+ let l = getcompletion('', 'cscope')
+ let cmds = ['add', 'find', 'help', 'kill', 'reset', 'show']
+ call assert_equal(cmds, l)
+ " using cmdline completion must not change the result
+ call feedkeys(":cscope find \<c-d>\<c-c>", 'xt')
+ let l = getcompletion('', 'cscope')
+ call assert_equal(cmds, l)
+ let keys = ['a', 'c', 'd', 'e', 'f', 'g', 'i', 's', 't']
+ let l = getcompletion('find ', 'cscope')
+ call assert_equal(keys, l)
+ endif
+
+ if has('signs')
+ sign define Testing linehl=Comment
+ let l = getcompletion('', 'sign')
+ let cmds = ['define', 'jump', 'list', 'place', 'undefine', 'unplace']
+ call assert_equal(cmds, l)
+ " using cmdline completion must not change the result
+ call feedkeys(":sign list \<c-d>\<c-c>", 'xt')
+ let l = getcompletion('', 'sign')
+ call assert_equal(cmds, l)
+ let l = getcompletion('list ', 'sign')
+ call assert_equal(['Testing'], l)
+ endif
+
" For others test if the name is recognized.
let names = ['buffer', 'environment', 'file_in_path',
\ 'mapping', 'shellcmd', 'tag', 'tag_listfiles', 'user']
- if has('cscope')
- call add(names, 'cscope')
- endif
if has('cmdline_hist')
call add(names, 'history')
endif
@@ -136,9 +163,6 @@ func Test_getcompletion()
if has('profile')
call add(names, 'syntime')
endif
- if has('signs')
- call add(names, 'sign')
- endif
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", "word\tfile\tcmd"], 'Xtags')
@@ -152,3 +176,11 @@ func Test_getcompletion()
call assert_fails('call getcompletion("", "burp")', 'E475:')
endfunc
+
+func Test_expand_star_star()
+ call mkdir('a/b', 'p')
+ call writefile(['asdfasdf'], 'a/b/fileXname')
+ call feedkeys(":find **/fileXname\<Tab>\<CR>", 'xt')
+ call assert_equal('find a/b/fileXname', getreg(':'))
+ call delete('a', 'rf')
+endfunc
diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim
index fa3ebd757e..66656e1d0a 100644
--- a/src/nvim/testdir/test_window_id.vim
+++ b/src/nvim/testdir/test_window_id.vim
@@ -3,17 +3,22 @@
func Test_win_getid()
edit one
let id1 = win_getid()
+ let w:one = 'one'
split two
let id2 = win_getid()
let bufnr2 = bufnr('%')
+ let w:two = 'two'
split three
let id3 = win_getid()
+ let w:three = 'three'
tabnew
edit four
let id4 = win_getid()
+ let w:four = 'four'
split five
let id5 = win_getid()
let bufnr5 = bufnr('%')
+ let w:five = 'five'
tabnext
wincmd w
@@ -28,6 +33,9 @@ func Test_win_getid()
call assert_equal("three", expand("%"))
call assert_equal(id3, win_getid())
let nr3 = winnr()
+ call assert_equal('one', getwinvar(id1, 'one'))
+ call assert_equal('two', getwinvar(id2, 'two'))
+ call assert_equal('three', getwinvar(id3, 'three'))
tabnext
call assert_equal("five", expand("%"))
call assert_equal(id5, win_getid())
@@ -36,7 +44,14 @@ func Test_win_getid()
call assert_equal("four", expand("%"))
call assert_equal(id4, win_getid())
let nr4 = winnr()
+ call assert_equal('four', getwinvar(id4, 'four'))
+ call assert_equal('five', getwinvar(id5, 'five'))
+ call settabwinvar(1, id2, 'two', '2')
+ call setwinvar(id4, 'four', '4')
tabnext
+ call assert_equal('4', gettabwinvar(2, id4, 'four'))
+ call assert_equal('five', gettabwinvar(2, id5, 'five'))
+ call assert_equal('2', getwinvar(id2, 'two'))
exe nr1 . "wincmd w"
call assert_equal(id1, win_getid())
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index f03d8b87fa..f252b00be2 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -22,10 +22,10 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/strings.h"
+#include "nvim/ui_bridge.h"
#include "nvim/ugrid.h"
#include "nvim/tui/input.h"
#include "nvim/tui/tui.h"
-#include "nvim/tui/ui_bridge.h"
// Space reserved in the output buffer to restore the cursor to normal when
// flushing. No existing terminal will require 32 bytes to do that.
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 648d633e07..eb500414a7 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -397,14 +397,14 @@ static void send_output(uint8_t **ptr)
size_t clen = (size_t)mb_ptr2len(p);
UI_CALL(put, p, (size_t)clen);
col++;
- if (utf_ambiguous_width(*p)) {
- pending_cursor_update = true;
- flush_cursor_update();
- } else if (mb_ptr2cells(p) > 1) {
+ if (mb_ptr2cells(p) > 1) {
// double cell character, blank the next cell
UI_CALL(put, NULL, 0);
col++;
}
+ if (utf_ambiguous_width(utf_ptr2char(p))) {
+ pending_cursor_update = true;
+ }
if (col >= width) {
ui_linefeed();
}
diff --git a/src/nvim/tui/ui_bridge.c b/src/nvim/ui_bridge.c
index 48f4b1bda6..cc27c734e0 100644
--- a/src/nvim/tui/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -1,4 +1,5 @@
-// UI wrapper for the built-in TUI. Sends UI requests to the TUI thread.
+// UI wrapper that sends UI requests to the UI thread.
+// Used by the built-in TUI and external libnvim-based UIs.
#include <assert.h>
#include <stdbool.h>
@@ -9,11 +10,11 @@
#include "nvim/vim.h"
#include "nvim/ui.h"
#include "nvim/memory.h"
+#include "nvim/ui_bridge.h"
#include "nvim/ugrid.h"
-#include "nvim/tui/ui_bridge.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "tui/ui_bridge.c.generated.h"
+# include "ui_bridge.c.generated.h"
#endif
#define UI(b) (((UIBridgeData *)b)->ui)
diff --git a/src/nvim/tui/ui_bridge.h b/src/nvim/ui_bridge.h
index 003ed3c2c1..9e4bf9f2a7 100644
--- a/src/nvim/tui/ui_bridge.h
+++ b/src/nvim/ui_bridge.h
@@ -1,6 +1,7 @@
-// Bridge used for communication between a builtin UI thread and nvim core
-#ifndef NVIM_TUI_UI_BRIDGE_H
-#define NVIM_TUI_UI_BRIDGE_H
+// Bridge for communication between a UI thread and nvim core.
+// Used by the built-in TUI and external libnvim-based UIs.
+#ifndef NVIM_UI_BRIDGE_H
+#define NVIM_UI_BRIDGE_H
#include <uv.h>
@@ -39,6 +40,6 @@ struct ui_bridge_data {
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "tui/ui_bridge.h.generated.h"
+# include "ui_bridge.h.generated.h"
#endif
-#endif // NVIM_TUI_UI_BRIDGE_H
+#endif // NVIM_UI_BRIDGE_H
diff --git a/src/nvim/version.c b/src/nvim/version.c
index f2f81b5614..e6ef600f8c 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -167,7 +167,7 @@ static int included_patches[] = {
// 2277,
// 2276,
// 2275,
- // 2274,
+ 2274,
// 2273,
// 2272,
// 2271 NA
@@ -236,7 +236,7 @@ static int included_patches[] = {
// 2208,
// 2207 NA
// 2206 NA
- // 2205,
+ 2205,
// 2204,
// 2203 NA
// 2202 NA
@@ -279,11 +279,11 @@ static int included_patches[] = {
// 2165,
// 2164,
// 2163,
- // 2162,
+ 2162,
// 2161,
// 2160,
// 2159,
- // 2158,
+ 2158,
// 2157 NA
// 2156 NA
// 2155 NA
@@ -546,9 +546,9 @@ static int included_patches[] = {
1898,
// 1897,
1896,
- // 1895,
+ 1895,
// 1894 NA
- // 1893,
+ 1893,
// 1892 NA
// 1891 NA
// 1890 NA
@@ -677,7 +677,7 @@ static int included_patches[] = {
// 1768,
// 1767 NA
// 1766 NA
- // 1765,
+ 1765,
// 1764 NA
1763,
// 1762,
@@ -729,7 +729,7 @@ static int included_patches[] = {
// 1717 NA
1716,
// 1715,
- // 1714,
+ 1714,
// 1713 NA
1712,
// 1711,
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 09e9e850a7..6bbd9b58ef 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -316,4 +316,7 @@ enum {
#define DIP_OPT 0x10 // also use "opt" directory in 'packpath'
#define DIP_NORTP 0x20 // do not use 'runtimepath'
+// Lowest number used for window ID. Cannot have this many windows per tab.
+#define LOWEST_WIN_ID 1000
+
#endif /* NVIM_VIM_H */
diff --git a/src/nvim/window.c b/src/nvim/window.c
index e9a66ad907..a259654d52 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -482,7 +482,7 @@ wingotofile:
}
static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize,
- long Prenum)
+ int64_t Prenum)
{
size_t len = xstrlcpy((char *)bufp, cmd, bufsize);
@@ -3694,7 +3694,7 @@ win_T *buf_jump_open_tab(buf_T *buf)
*/
static win_T *win_alloc(win_T *after, int hidden)
{
- static int last_win_id = 0;
+ static int last_win_id = LOWEST_WIN_ID - 1;
// allocate window structure and linesizes arrays
win_T *new_wp = xcalloc(1, sizeof(win_T));
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index 480f099b4d..3245e1b52d 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -164,7 +164,8 @@ describe('server -> client', function()
it('can communicate buffers, tabpages, and windows', function()
eq({1}, eval("rpcrequest(vim, 'nvim_list_tabpages')"))
- eq({1}, eval("rpcrequest(vim, 'nvim_list_wins')"))
+ -- Window IDs start at 1000 (LOWEST_WIN_ID in vim.h)
+ eq({1000}, eval("rpcrequest(vim, 'nvim_list_wins')"))
local buf = eval("rpcrequest(vim, 'nvim_list_bufs')")[1]
eq(1, buf)
diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua
index 295c763d74..fba9466b78 100644
--- a/test/functional/eval/timer_spec.lua
+++ b/test/functional/eval/timer_spec.lua
@@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen')
local ok, feed, eq, eval = helpers.ok, helpers.feed, helpers.eq, helpers.eval
local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run
local clear, execute, funcs = helpers.clear, helpers.execute, helpers.funcs
+local curbufmeths = helpers.curbufmeths
describe('timers', function()
before_each(function()
@@ -62,19 +63,76 @@ describe('timers', function()
end)
it('are paused when event processing is disabled', function()
- -- this is not the intended behavior, but at least there will
- -- not be a burst of queued up callbacks
- execute("call timer_start(50, 'MyHandler', {'repeat': 2})")
+ execute("call timer_start(50, 'MyHandler', {'repeat': -1})")
run(nil, nil, nil, 100)
local count = eval("g:val")
+ -- shows two line error message and thus invokes the return prompt.
+ -- if we start to allow event processing here, we need to change this test.
+ execute("throw 'fatal error'")
+ run(nil, nil, nil, 300)
+ feed("<cr>")
+ local diff = eval("g:val") - count
+ ok(0 <= diff and diff <= 4)
+ end)
+
+ it('are triggered in blocking getchar() call', function()
+ execute("call timer_start(50, 'MyHandler', {'repeat': -1})")
nvim_async("command", "let g:c = getchar()")
run(nil, nil, nil, 300)
feed("c")
- local diff = eval("g:val") - count
- ok(0 <= diff and diff <= 2)
+ local count = eval("g:val")
+ ok(count >= 5)
eq(99, eval("g:c"))
end)
+ it('can invoke redraw in blocking getchar() call', function()
+ local screen = Screen.new(40, 6)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ })
+
+ curbufmeths.set_lines(0, -1, true, {"ITEM 1", "ITEM 2"})
+ source([[
+ func! AddItem(timer)
+ call nvim_buf_set_lines(0, 2, 2, v:true, ['ITEM 3'])
+ redraw
+ endfunc
+ call timer_start(200, 'AddItem')
+ ]])
+ nvim_async("command", "let g:c2 = getchar()")
+
+ screen:expect([[
+ ITEM 1 |
+ ITEM 2 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ^ |
+ ]])
+
+ screen:sleep(200)
+ screen:expect([[
+ ITEM 1 |
+ ITEM 2 |
+ ITEM 3 |
+ {1:~ }|
+ {1:~ }|
+ ^ |
+ ]])
+
+ feed("3")
+ eq(51, eval("g:c2"))
+ screen:expect([[
+ ^ITEM 1 |
+ ITEM 2 |
+ ITEM 3 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
it('can be stopped', function()
local t = eval("timer_start(50, 'MyHandler', {'repeat': -1})")
eq(0,eval("g:val"))
diff --git a/test/functional/ex_cmds/arg_spec.lua b/test/functional/ex_cmds/arg_spec.lua
new file mode 100644
index 0000000000..e66dc62491
--- /dev/null
+++ b/test/functional/ex_cmds/arg_spec.lua
@@ -0,0 +1,30 @@
+local helpers = require("test.functional.helpers")(after_each)
+local eq, execute, funcs = helpers.eq, helpers.execute, helpers.funcs
+local ok = helpers.ok
+local clear = helpers.clear
+
+describe(":argument", function()
+ before_each(function()
+ clear()
+ end)
+
+ it("does not restart :terminal buffer", function()
+ execute("terminal")
+ helpers.feed([[<C-\><C-N>]])
+ execute("argadd")
+ helpers.feed([[<C-\><C-N>]])
+ local bufname_before = funcs.bufname("%")
+ local bufnr_before = funcs.bufnr("%")
+ helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity
+
+ execute("argument 1")
+ helpers.feed([[<C-\><C-N>]])
+
+ local bufname_after = funcs.bufname("%")
+ local bufnr_after = funcs.bufnr("%")
+ eq("\n["..bufname_before.."] ", helpers.eval('execute("args")'))
+ ok(funcs.line('$') > 1)
+ eq(bufname_before, bufname_after)
+ eq(bufnr_before, bufnr_after)
+ end)
+end)
diff --git a/test/functional/ex_cmds/edit_spec.lua b/test/functional/ex_cmds/edit_spec.lua
new file mode 100644
index 0000000000..9e197d7466
--- /dev/null
+++ b/test/functional/ex_cmds/edit_spec.lua
@@ -0,0 +1,26 @@
+local helpers = require("test.functional.helpers")(after_each)
+local eq, execute, funcs = helpers.eq, helpers.execute, helpers.funcs
+local ok = helpers.ok
+local clear = helpers.clear
+
+describe(":edit", function()
+ before_each(function()
+ clear()
+ end)
+
+ it("without arguments does not restart :terminal buffer", function()
+ execute("terminal")
+ helpers.feed([[<C-\><C-N>]])
+ local bufname_before = funcs.bufname("%")
+ local bufnr_before = funcs.bufnr("%")
+ helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity
+
+ execute("edit")
+
+ local bufname_after = funcs.bufname("%")
+ local bufnr_after = funcs.bufnr("%")
+ ok(funcs.line('$') > 1)
+ eq(bufname_before, bufname_after)
+ eq(bufnr_before, bufnr_after)
+ end)
+end)
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index 6f929f17e4..09b4eaa8d5 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -13,13 +13,19 @@ describe(':terminal', function()
clear()
screen = Screen.new(50, 4)
screen:attach({rgb=false})
+ -- shell-test.c is a fake shell that prints its arguments and exits.
nvim('set_option', 'shell', nvim_dir..'/shell-test')
nvim('set_option', 'shellcmdflag', 'EXE')
-
end)
+ -- Invokes `:terminal {cmd}` using a fake shell (shell-test.c) which prints
+ -- the {cmd} and exits immediately .
+ local function terminal_run_fake_shell_cmd(cmd)
+ execute("terminal "..(cmd and cmd or ""))
+ end
+
it('with no argument, acts like termopen()', function()
- execute('terminal')
+ terminal_run_fake_shell_cmd()
wait()
screen:expect([[
ready $ |
@@ -30,7 +36,7 @@ describe(':terminal', function()
end)
it('executes a given command through the shell', function()
- execute('terminal echo hi')
+ terminal_run_fake_shell_cmd('echo hi')
wait()
screen:expect([[
ready $ echo hi |
@@ -41,7 +47,7 @@ describe(':terminal', function()
end)
it('allows quotes and slashes', function()
- execute([[terminal echo 'hello' \ "world"]])
+ terminal_run_fake_shell_cmd([[echo 'hello' \ "world"]])
wait()
screen:expect([[
ready $ echo 'hello' \ "world" |
@@ -58,4 +64,16 @@ describe(':terminal', function()
-- Verify that BufNew actually fired (else the test is invalid).
eq('foo', eval('&shell'))
end)
+
+ it('ignores writes if the backing stream closes', function()
+ terminal_run_fake_shell_cmd()
+ helpers.feed('iiXXXXXXX')
+ wait()
+ -- Race: Though the shell exited (and streams were closed by SIGCHLD
+ -- handler), :terminal cleanup is pending on the main-loop.
+ -- This write should be ignored (not crash, #5445).
+ helpers.feed('iiYYYYYYY')
+ wait()
+ end)
+
end)
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 27f00e8550..60f989d701 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -313,7 +313,7 @@ describe("tui 't_Co' (terminal colors)", function()
-- This is ugly because :term/termopen() forces TERM=xterm-256color.
-- TODO: Revisit this after jobstart/termopen accept `env` dict.
screen = thelpers.screen_setup(0, string.format(
- [=[['sh', '-c', 'TERM=%s %s %s -u NONE -i NONE --cmd "silent set noswapfile"']]=],
+ [=[['sh', '-c', 'LANG=C TERM=%s %s %s -u NONE -i NONE --cmd "silent set noswapfile"']]=],
term,
(colorterm ~= nil and "COLORTERM="..colorterm or ""),
helpers.nvim_prog))