aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAIN.md6
-rw-r--r--runtime/filetype.vim7
-rw-r--r--runtime/lua/vim/filetype.lua1
-rwxr-xr-xscripts/bump-deps.sh104
-rw-r--r--scripts/bump_deps.lua343
-rw-r--r--src/nvim/buffer.c39
-rw-r--r--src/nvim/buffer.h7
-rw-r--r--src/nvim/change.c6
-rw-r--r--src/nvim/diff.c14
-rw-r--r--src/nvim/edit.c42
-rw-r--r--src/nvim/ex_cmds.c8
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/ex_docmd.h1
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/fileio.c38
-rw-r--r--src/nvim/getchar.c21
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/memline.c159
-rw-r--r--src/nvim/memory.c2
-rw-r--r--src/nvim/option.c66
-rw-r--r--src/nvim/quickfix.c4
-rw-r--r--src/nvim/state.c5
-rw-r--r--src/nvim/syntax.c79
-rw-r--r--src/nvim/testdir/test_autocmd.vim34
-rw-r--r--src/nvim/testdir/test_diffmode.vim37
-rw-r--r--src/nvim/testdir/test_edit.vim23
-rw-r--r--src/nvim/testdir/test_filetype.vim2
-rw-r--r--src/nvim/testdir/test_highlight.vim8
-rw-r--r--src/nvim/testdir/test_ins_complete.vim19
-rw-r--r--src/nvim/testdir/test_registers.vim18
-rw-r--r--src/nvim/testdir/test_trycatch.vim27
-rw-r--r--src/nvim/window.c68
-rw-r--r--test/functional/editor/macro_spec.lua47
-rw-r--r--test/functional/editor/mode_insert_spec.lua24
-rw-r--r--test/functional/ui/diff_spec.lua127
-rw-r--r--test/functional/ui/highlight_spec.lua22
-rw-r--r--test/unit/buffer_spec.lua42
37 files changed, 1162 insertions, 296 deletions
diff --git a/MAINTAIN.md b/MAINTAIN.md
index 7df91b7d57..2b390270a9 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -76,6 +76,12 @@ These "bundled" dependencies can be updated by bumping their versions in `third-
- [lua-compat](https://github.com/keplerproject/lua-compat-5.3)
- [tree-sitter](https://github.com/tree-sitter/tree-sitter)
+`scripts/bump-dep.sh` is a script that can automate this process for `LuaJIT`, `Luv`, `libuv` & `tree-sitter`. See usage guide:
+ - Run `./scripts/bump-deps.sh --dep Luv --version 1.43.0-0` to update a dependency.
+ See `./scripts/bump-deps.sh -h` for more detailed usage
+ - Run `./scripts/bump-deps.sh --pr` to create a pr
+ To generate the default PR title and body, the script uses the most recent commit (not in `master`) with prefix `build(deps): `
+
These dependencies are "vendored" (inlined), we need to update the sources manually:
- [libmpack](https://github.com/libmpack/libmpack)
- [xdiff](https://github.com/git/git/tree/master/xdiff)
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index d7a3ec182f..bc94ea848c 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1348,9 +1348,10 @@ au BufNewFile,BufRead *.pm
au BufNewFile,BufRead *.pod setf pod
" Php, php3, php4, etc.
-" Also Phtml (was used for PHP 2 in the past)
-" Also .ctp for Cake template file
-au BufNewFile,BufRead *.php,*.php\d,*.phtml,*.ctp setf php
+" Also Phtml (was used for PHP 2 in the past).
+" Also .ctp for Cake template file.
+" Also .phpt for php tests.
+au BufNewFile,BufRead *.php,*.php\d,*.phtml,*.ctp,*.phpt setf php
" PHP config
au BufNewFile,BufRead php.ini-* setf dosini
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 0466057d72..6d8f734bed 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -457,6 +457,7 @@ local extension = {
al = "perl",
ctp = "php",
php = "php",
+ phpt = "php",
phtml = "php",
pike = "pike",
pmod = "pike",
diff --git a/scripts/bump-deps.sh b/scripts/bump-deps.sh
new file mode 100755
index 0000000000..85c7f72700
--- /dev/null
+++ b/scripts/bump-deps.sh
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+set -e
+set -u
+# Use privileged mode, which e.g. skips using CDPATH.
+set -p
+
+# Ensure that the user has a bash that supports -A
+if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then
+ echo >&2 "error: script requires bash 4+ (you have ${BASH_VERSION})."
+ exit 1
+fi
+
+readonly NVIM_SOURCE_DIR="${NVIM_SOURCE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
+readonly VIM_SOURCE_DIR_DEFAULT="${NVIM_SOURCE_DIR}/.vim-src"
+readonly VIM_SOURCE_DIR="${VIM_SOURCE_DIR:-${VIM_SOURCE_DIR_DEFAULT}}"
+BASENAME="$(basename "${0}")"
+readonly BASENAME
+
+usage() {
+ echo "Bump Neovim dependencies"
+ echo
+ echo "Usage: ${BASENAME} [ -h | --pr | --branch=<dep> | --dep=<dependency> ]"
+ echo
+ echo "Options:"
+ echo " -h show this message and exit."
+ echo " --pr submit pr for bumping deps."
+ echo " --branch=<dep> create a branch bump-<dep> from current branch."
+ echo " --dep=<dependency> bump to a specific release or tag."
+ echo
+ echo "Dependency Options:"
+ echo " --version=<tag> bump to a specific release or tag."
+ echo " --commit=<hash> bump to a specific commit."
+ echo " --HEAD bump to a current head."
+ echo
+ echo " <dependency> is one of:"
+ echo " \"LuaJIT\", \"libuv\", \"Luv\", \"tree-sitter\""
+}
+
+# Checks if a program is in the user's PATH, and is executable.
+check_executable() {
+ test -x "$(command -v "${1}")"
+}
+
+require_executable() {
+ if ! check_executable "${1}"; then
+ echo >&2 "${BASENAME}: '${1}' not found in PATH or not executable."
+ exit 1
+ fi
+}
+
+require_executable "nvim"
+
+if [ $# -eq 0 ]; then
+ usage
+ exit 1
+fi
+
+PARSED_ARGS=$(getopt -a -n "$BASENAME" -o h --long pr,branch:,dep:,version:,commit:,HEAD -- "$@")
+
+DEPENDENCY=""
+eval set -- "$PARSED_ARGS"
+while :; do
+ case "$1" in
+ -h)
+ usage
+ exit 0
+ ;;
+ --pr)
+ nvim -es +"lua require('scripts.bump_deps').submit_pr()"
+ exit 0
+ ;;
+ --branch)
+ DEP=$2
+ nvim -es +"lua require('scripts.bump_deps').create_branch('$DEP')"
+ exit 0
+ ;;
+ --dep)
+ DEPENDENCY=$2
+ shift 2
+ ;;
+ --version)
+ VERSION=$2
+ nvim -es +"lua require('scripts.bump_deps').version('$DEPENDENCY', '$VERSION')"
+ exit 0
+ ;;
+ --commit)
+ COMMIT=$2
+ nvim -es +"lua require('scripts.bump_deps').commit('$DEPENDENCY', '$COMMIT')"
+ exit 0
+ ;;
+ --HEAD)
+ nvim -es +"lua require('scripts.bump_deps').head('$DEPENDENCY')"
+ exit 0
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+usage
+exit 1
+
+# vim: et sw=2
diff --git a/scripts/bump_deps.lua b/scripts/bump_deps.lua
new file mode 100644
index 0000000000..2ecbb2e658
--- /dev/null
+++ b/scripts/bump_deps.lua
@@ -0,0 +1,343 @@
+-- Usage:
+-- # bump to version
+-- nvim -es +"lua require('scripts.bump_deps').version(dependency, version_tag)"
+--
+-- # bump to commit
+-- nvim -es +"lua require('scripts.bump_deps').commit(dependency, commit_hash)"
+--
+-- # bump to HEAD
+-- nvim -es +"lua require('scripts.bump_deps').head(dependency)"
+--
+-- # submit PR
+-- nvim -es +"lua require('scripts.bump_deps').submit_pr()"
+--
+-- # create branch
+-- nvim -es +"lua require('scripts.bump_deps').create_branch()"
+
+local M = {}
+
+local _trace = false
+local required_branch_prefix = "bump-"
+local commit_prefix = "build(deps): "
+
+-- Print message
+local function p(s)
+ vim.cmd("set verbose=1")
+ vim.api.nvim_echo({ { s, "" } }, false, {})
+ vim.cmd("set verbose=0")
+end
+
+local function die()
+ p("")
+ vim.cmd("cquit 1")
+end
+
+-- Executes and returns the output of `cmd`, or nil on failure.
+-- if die_on_fail is true, process dies with die_msg on failure
+--
+-- Prints `cmd` if `trace` is enabled.
+local function _run(cmd, die_on_fail, die_msg)
+ if _trace then
+ p("run: " .. vim.inspect(cmd))
+ end
+ local rv = vim.trim(vim.fn.system(cmd)) or ""
+ if vim.v.shell_error ~= 0 then
+ if die_on_fail then
+ if _trace then
+ p(rv)
+ end
+ p(die_msg)
+ die()
+ end
+ return nil
+ end
+ return rv
+end
+
+-- Run a command, return nil on failure
+local function run(cmd)
+ return _run(cmd, false, "")
+end
+
+-- Run a command, die on failure with err_msg
+local function run_die(cmd, err_msg)
+ return _run(cmd, true, err_msg)
+end
+
+local function require_executable(cmd)
+ local cmd_path = run_die({ "command", "-v", cmd }, cmd .. " not found!")
+ run_die({ "test", "-x", cmd_path }, cmd .. " is not executable")
+end
+
+local function rm_file_if_present(path_to_file)
+ run({ "rm", "-f", path_to_file })
+end
+
+local nvim_src_dir = vim.fn.getcwd()
+local temp_dir = nvim_src_dir .. "/tmp"
+run({ "mkdir", "-p", temp_dir })
+
+local function get_dependency(dependency_name)
+ local dependency_table = {
+ ["LuaJIT"] = {
+ repo = "LuaJIT/LuaJIT",
+ symbol = "LUAJIT",
+ },
+ ["libuv"] = {
+ repo = "libuv/libuv",
+ symbol = "LIBUV",
+ },
+ ["Luv"] = {
+ repo = "luvit/luv",
+ symbol = "LUV",
+ },
+ ["tree-sitter"] = {
+ repo = "tree-sitter/tree-sitter",
+ symbol = "TREESITTER",
+ },
+ }
+ local dependency = dependency_table[dependency_name]
+ if dependency == nil then
+ p("Not a dependency: " .. dependency_name)
+ die()
+ end
+ dependency.name = dependency_name
+ return dependency
+end
+
+local function get_gh_commit_sha(repo, ref)
+ require_executable("gh")
+
+ local sha = run_die(
+ { "gh", "api", "repos/" .. repo .. "/commits/" .. ref, "--jq", ".sha" },
+ "Failed to get commit hash from GitHub. Not a valid ref?"
+ )
+ return sha
+end
+
+local function get_archive_info(repo, ref)
+ require_executable("curl")
+
+ local archive_name = ref .. ".tar.gz"
+ local archive_path = temp_dir .. "/" .. archive_name
+ local archive_url = "https://github.com/" .. repo .. "/archive/" .. archive_name
+
+ rm_file_if_present(archive_path)
+ run_die({ "curl", "-sL", archive_url, "-o", archive_path }, "Failed to download archive from GitHub")
+
+ local archive_sha = run({ "sha256sum", archive_path }):gmatch("%w+")()
+ return { url = archive_url, sha = archive_sha }
+end
+
+local function write_cmakelists_line(symbol, kind, value)
+ require_executable("sed")
+
+ local cmakelists_path = nvim_src_dir .. "/" .. "third-party/CMakeLists.txt"
+ run_die({
+ "sed",
+ "-i",
+ "-e",
+ "s/set(" .. symbol .. "_" .. kind .. ".*$" .. "/set(" .. symbol .. "_" .. kind .. " " .. value .. ")" .. "/",
+ cmakelists_path,
+ }, "Failed to write " .. cmakelists_path)
+end
+
+local function explicit_create_branch(dep)
+ require_executable("git")
+
+ local checked_out_branch = run({ "git", "rev-parse", "--abbrev-ref", "HEAD" })
+ if checked_out_branch ~= "master" then
+ p("Not on master!")
+ die()
+ end
+ run_die({ "git", "checkout", "-b", "bump-" .. dep }, "git failed to create branch")
+end
+
+local function verify_branch(new_branch_suffix)
+ require_executable("git")
+
+ local checked_out_branch = run({ "git", "rev-parse", "--abbrev-ref", "HEAD" })
+ if not checked_out_branch:match("^" .. required_branch_prefix) then
+ p("Current branch '" .. checked_out_branch .. "' doesn't seem to start with " .. required_branch_prefix)
+ p("Checking out to bump-" .. new_branch_suffix)
+ explicit_create_branch(new_branch_suffix)
+ end
+end
+
+local function update_cmakelists(dependency, archive, comment)
+ require_executable("git")
+
+ verify_branch(dependency.name)
+
+ local changed_file = nvim_src_dir .. "/" .. "third-party/CMakeLists.txt"
+
+ p("Updating " .. dependency.name .. " to " .. archive.url .. "\n")
+ write_cmakelists_line(dependency.symbol, "URL", archive.url:gsub("/", "\\/"))
+ write_cmakelists_line(dependency.symbol, "SHA256", archive.sha)
+ run_die(
+ { "git", "commit", changed_file, "-m", commit_prefix .. "bump " .. dependency.name .. " to " .. comment },
+ "git failed to commit"
+ )
+end
+
+local function verify_cmakelists_committed()
+ require_executable("git")
+
+ local cmakelists_path = nvim_src_dir .. "/" .. "third-party/CMakeLists.txt"
+ run_die({ "git", "diff", "--quiet", "HEAD", "--", cmakelists_path }, cmakelists_path .. " has uncommitted changes")
+end
+
+local function warn_luv_symbol()
+ p("warning: " .. get_dependency("Luv").symbol .. "_VERSION will not be updated")
+end
+
+-- return first 9 chars of commit
+local function short_commit(commit)
+ return string.sub(commit, 1, 9)
+end
+
+-- TODO: remove hardcoded fork
+local function gh_pr(pr_title, pr_body)
+ require_executable("gh")
+
+ local pr_url = run_die({
+ "gh",
+ "pr",
+ "create",
+ "--title",
+ pr_title,
+ "--body",
+ pr_body,
+ }, "Failed to create PR")
+ return pr_url
+end
+
+local function find_git_remote(fork)
+ require_executable("git")
+
+ local remotes = run({ "git", "remote", "-v" })
+ local git_remote = ""
+ for remote in remotes:gmatch("[^\r\n]+") do
+ local words = {}
+ for word in remote:gmatch("%w+") do
+ table.insert(words, word)
+ end
+ local match = words[1]:match("/github.com[:/]neovim/neovim/")
+ if fork == "fork" then
+ match = not match
+ end
+ if match and words[3] == "(fetch)" then
+ git_remote = words[0]
+ break
+ end
+ end
+ if git_remote == "" then
+ git_remote = "origin"
+ end
+ return git_remote
+end
+
+local function create_pr(pr_title, pr_body)
+ require_executable("git")
+
+ local push_first = true
+
+ local checked_out_branch = run({ "git", "rev-parse", "--abbrev-ref", "HEAD" })
+ if push_first then
+ local push_remote = run({ "git", "config", "--get", "branch." .. checked_out_branch .. ".pushRemote" })
+ if push_remote == nil then
+ push_remote = run({ "git", "config", "--get", "remote.pushDefault" })
+ if push_remote == nil then
+ push_remote = run({ "git", "config", "--get", "branch." .. checked_out_branch .. ".remote" })
+ if push_remote == nil or push_remote == find_git_remote(nil) then
+ push_remote = find_git_remote("fork")
+ end
+ end
+ end
+
+ p("Pushing to " .. push_remote .. "/" .. checked_out_branch)
+ run_die({ "git", "push", push_remote, checked_out_branch }, "Git failed to push")
+ end
+
+ local pr_url = gh_pr(pr_title, pr_body)
+ p("\nCreated PR: " .. pr_url .. "\n")
+end
+
+function M.commit(dependency_name, commit)
+ local dependency = get_dependency(dependency_name)
+ verify_cmakelists_committed()
+ local commit_sha = get_gh_commit_sha(dependency.repo, commit)
+ if commit_sha ~= commit then
+ p("Not a commit: " .. commit .. ". Did you mean version?")
+ die()
+ end
+ local archive = get_archive_info(dependency.repo, commit)
+ if dependency_name == "Luv" then
+ warn_luv_symbol()
+ end
+ update_cmakelists(dependency, archive, short_commit(commit))
+end
+
+function M.version(dependency_name, version)
+ local dependency = get_dependency(dependency_name)
+ verify_cmakelists_committed()
+ local commit_sha = get_gh_commit_sha(dependency.repo, version)
+ if commit_sha == version then
+ p("Not a version: " .. version .. ". Did you mean commit?")
+ die()
+ end
+ local archive = get_archive_info(dependency.repo, version)
+ if dependency_name == "Luv" then
+ write_cmakelists_line(dependency.symbol, "VERSION", version)
+ end
+ update_cmakelists(dependency, archive, version)
+end
+
+function M.head(dependency_name)
+ local dependency = get_dependency(dependency_name)
+ verify_cmakelists_committed()
+ local commit_sha = get_gh_commit_sha(dependency.repo, "HEAD")
+ local archive = get_archive_info(dependency.repo, commit_sha)
+ if dependency_name == "Luv" then
+ warn_luv_symbol()
+ end
+ update_cmakelists(dependency, archive, "HEAD - " .. short_commit(commit_sha))
+end
+
+function M.create_branch(dep)
+ explicit_create_branch(dep)
+end
+
+function M.submit_pr()
+ require_executable("git")
+
+ verify_branch("deps")
+
+ local nvim_remote = find_git_remote(nil)
+ local relevant_commit = run_die({
+ "git",
+ "log",
+ "--grep=" .. commit_prefix,
+ "--reverse",
+ "--format='%s'",
+ nvim_remote .. "/master..HEAD",
+ "-1",
+ }, "Failed to fetch commits")
+
+ local pr_title
+ local pr_body
+
+ if relevant_commit == "" then
+ pr_title = commit_prefix .. "bump some dependencies"
+ pr_body = "bump some dependencies"
+ else
+ relevant_commit = relevant_commit:gsub("'", "")
+ pr_title = relevant_commit
+ pr_body = relevant_commit:gsub(commit_prefix:gsub("%(", "%%("):gsub("%)", "%%)"), "")
+ end
+ pr_body = pr_body .. "\n\n(add explanations if needed)"
+ p(pr_title .. "\n" .. pr_body .. "\n")
+ create_pr(pr_title, pr_body)
+end
+
+return M
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index bf592a626d..4ec23244cd 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -174,7 +174,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
if (ml_open(curbuf) == FAIL) {
// There MUST be a memfile, otherwise we can't do anything
// If we can't create one for the current buffer, take another buffer
- close_buffer(NULL, curbuf, 0, false);
+ close_buffer(NULL, curbuf, 0, false, false);
curbuf = NULL;
FOR_ALL_BUFFERS(buf) {
@@ -402,8 +402,10 @@ bool buf_valid(buf_T *buf)
/// 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.
+/// @param ignore_abort
+/// If true, don't abort even when aborting() returns true.
/// @return true when we got to the end and b_nwindows was decremented.
-bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
+bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool ignore_abort)
{
bool unload_buf = (action != 0);
bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
@@ -494,7 +496,8 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
return false;
}
}
- if (aborting()) { // autocmds may abort script processing
+ // autocmds may abort script processing
+ if (!ignore_abort && aborting()) {
return false;
}
}
@@ -552,14 +555,16 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf->b_nwindows = nwindows;
- buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
+ buf_freeall(buf, ((del_buf ? BFA_DEL : 0)
+ + (wipe_buf ? BFA_WIPE : 0)
+ + (ignore_abort ? BFA_IGNORE_ABORT : 0)));
if (!bufref_valid(&bufref)) {
// Autocommands may have deleted the buffer.
return false;
}
- if (aborting()) {
- // Autocmds may abort script processing.
+ // autocmds may abort script processing.
+ if (!ignore_abort && aborting()) {
return false;
}
@@ -660,9 +665,10 @@ void buf_clear(void)
/// buf_freeall() - free all things allocated for a buffer that are related to
/// the file. Careful: get here with "curwin" NULL when exiting.
///
-/// @param flags BFA_DEL buffer is going to be deleted
-/// BFA_WIPE buffer is going to be wiped out
-/// BFA_KEEP_UNDO do not free undo information
+/// @param flags BFA_DEL buffer is going to be deleted
+/// BFA_WIPE buffer is going to be wiped out
+/// BFA_KEEP_UNDO do not free undo information
+/// BFA_IGNORE_ABORT don't abort even when aborting() returns true
void buf_freeall(buf_T *buf, int flags)
{
bool is_curbuf = (buf == curbuf);
@@ -706,7 +712,8 @@ void buf_freeall(buf_T *buf, int flags)
goto_tabpage_win(the_curtab, the_curwin);
unblock_autocmds();
}
- if (aborting()) { // autocmds may abort script processing
+ // autocmds may abort script processing
+ if ((flags & BFA_IGNORE_ABORT) == 0 && aborting()) {
return;
}
@@ -877,7 +884,7 @@ void handle_swap_exists(bufref_T *old_curbuf)
// open a new, empty buffer.
swap_exists_action = SEA_NONE; // don't want it again
swap_exists_did_quit = true;
- close_buffer(curwin, curbuf, DOBUF_UNLOAD, false);
+ close_buffer(curwin, curbuf, DOBUF_UNLOAD, false, false);
if (old_curbuf == NULL
|| !bufref_valid(old_curbuf)
|| old_curbuf->br_buf == curbuf) {
@@ -1074,7 +1081,7 @@ static int empty_curbuf(int close_others, int forceit, int action)
// the old one. But do_ecmd() may have done that already, check
// if the buffer still exists.
if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0) {
- close_buffer(NULL, buf, action, false);
+ close_buffer(NULL, buf, action, false, false);
}
if (!close_others) {
@@ -1259,7 +1266,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
if (buf != curbuf) {
close_windows(buf, false);
if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0) {
- close_buffer(NULL, buf, action, false);
+ close_buffer(NULL, buf, action, false, false);
}
return OK;
}
@@ -1485,7 +1492,7 @@ void set_curbuf(buf_T *buf, int action)
? action
: (action == DOBUF_GOTO && !buf_hide(prevbuf)
&& !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0,
- false);
+ false, false);
if (curwin != previouswin && win_valid(previouswin)) {
// autocommands changed curwin, Grr!
curwin = previouswin;
@@ -2805,7 +2812,7 @@ int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message)
return FAIL;
}
// delete from the list
- close_buffer(NULL, obuf, DOBUF_WIPE, false);
+ close_buffer(NULL, obuf, DOBUF_WIPE, false, false);
}
sfname = vim_strsave(sfname);
#ifdef USE_FNAME_CASE
@@ -5650,7 +5657,7 @@ void wipe_buffer(buf_T *buf, bool aucmd)
// Don't trigger BufDelete autocommands here.
block_autocmds();
}
- close_buffer(NULL, buf, DOBUF_WIPE, false);
+ close_buffer(NULL, buf, DOBUF_WIPE, false, true);
if (!aucmd) {
unblock_autocmds();
}
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 850ab175a5..7f4bbcc9e5 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -58,9 +58,10 @@ enum dobuf_start_values {
// flags for buf_freeall()
enum bfa_values {
- BFA_DEL = 1, // buffer is going to be deleted
- BFA_WIPE = 2, // buffer is going to be wiped out
- BFA_KEEP_UNDO = 4, // do not free undo information
+ BFA_DEL = 1, // buffer is going to be deleted
+ BFA_WIPE = 2, // buffer is going to be wiped out
+ BFA_KEEP_UNDO = 4, // do not free undo information
+ BFA_IGNORE_ABORT = 8, // do not abort for aborting()
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 0644b1d601..44abd69733 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -286,9 +286,11 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
set_topline(wp, wp->w_topline);
}
- // Relative numbering may require updating more.
+ // If lines have been added or removed, relative numbering always
+ // requires a redraw.
if (wp->w_p_rnu && xtra != 0) {
- redraw_later(wp, SOME_VALID);
+ wp->w_last_cursor_lnum_rnu = 0;
+ redraw_later(wp, VALID);
}
// Cursor line highlighting probably need to be updated with
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index a6bbe40999..0b55fb877c 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -743,11 +743,16 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
for (char_u *s = ml_get_buf(buf, lnum, false); *s != NUL;) {
if (diff_flags & DIFF_ICASE) {
+ int c;
char_u cbuf[MB_MAXBYTES + 1];
- // xdiff doesn't support ignoring case, fold-case the text.
- int c = utf_ptr2char(s);
- c = utf_fold(c);
+ if (*s == NL) {
+ c = NUL;
+ } else {
+ // xdiff doesn't support ignoring case, fold-case the text.
+ c = utf_ptr2char(s);
+ c = utf_fold(c);
+ }
const int orig_len = utfc_ptr2len(s);
if (utf_char2bytes(c, cbuf) != orig_len) {
// TODO(Bram): handle byte length difference
@@ -759,7 +764,8 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
s += orig_len;
len += orig_len;
} else {
- ptr[len++] = *s++;
+ ptr[len++] = *s == NL ? NUL : *s;
+ s++;
}
}
ptr[len++] = NL;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index da0b577056..3eb4ab9517 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -390,12 +390,9 @@ static void insert_enter(InsertState *s)
trigger_modechanged();
stop_insert_mode = false;
- // Need to recompute the cursor position, it might move when the cursor
- // is on a TAB or special character.
- // ptr2cells() treats a TAB character as double-width.
- if (ptr2cells(get_cursor_pos_ptr()) > 1) {
- curwin->w_valid &= ~VALID_VIRTCOL;
- curs_columns(curwin, true);
+ // need to position cursor again when on a TAB
+ if (gchar_cursor() == TAB) {
+ curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
}
// Enable langmap or IME, indicated by 'iminsert'.
@@ -1396,6 +1393,7 @@ static void insert_do_complete(InsertState *s)
compl_cont_status = 0;
}
compl_busy = false;
+ can_si = true; // allow smartindenting
}
static void insert_do_cindent(InsertState *s)
@@ -7330,21 +7328,21 @@ static void mb_replace_pop_ins(int cc)
// Not a multi-byte char, put it back.
replace_push(c);
break;
+ }
+
+ buf[0] = c;
+ assert(n > 1);
+ for (i = 1; i < n; i++) {
+ buf[i] = replace_pop();
+ }
+ if (utf_iscomposing(utf_ptr2char(buf))) {
+ ins_bytes_len(buf, n);
} else {
- buf[0] = c;
- assert(n > 1);
- for (i = 1; i < n; i++) {
- buf[i] = replace_pop();
- }
- if (utf_iscomposing(utf_ptr2char(buf))) {
- ins_bytes_len(buf, n);
- } else {
- // Not a composing char, put it back.
- for (i = n - 1; i >= 0; i--) {
- replace_push(buf[i]);
- }
- break;
+ // Not a composing char, put it back.
+ for (i = n - 1; i >= 0; i--) {
+ replace_push(buf[i]);
}
+ break;
}
}
}
@@ -8054,8 +8052,10 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
State = NORMAL;
trigger_modechanged();
- // need to position cursor again (e.g. when on a TAB )
- changed_cline_bef_curs();
+ // need to position cursor again when on a TAB
+ if (gchar_cursor() == TAB) {
+ curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
+ }
setmouse();
ui_cursor_shape(); // may show different cursor shape
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 5d5b342c35..98aabe89b3 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2527,9 +2527,9 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
// Close the link to the current buffer. This will set
// oldwin->w_buffer to NULL.
u_sync(false);
- const bool did_decrement = close_buffer(oldwin, curbuf,
- (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
- false);
+ const bool did_decrement
+ = close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
+ false, false);
// Autocommands may have closed the window.
if (win_valid(the_curwin)) {
@@ -2875,7 +2875,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
redraw_curbuf_later(NOT_VALID); // redraw this buffer later
}
- if (p_im) {
+ if (p_im && (State & INSERT) == 0) {
need_start_insertmode = true;
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index bb6f3ba395..1f17101aca 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1248,6 +1248,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
const int save_msg_scroll = msg_scroll;
cmdmod_T save_cmdmod;
const int save_reg_executing = reg_executing;
+ const bool save_pending_end_reg_executing = pending_end_reg_executing;
char_u *cmd;
memset(&ea, 0, sizeof(ea));
@@ -2018,6 +2019,7 @@ doend:
undo_cmdmod(&ea, save_msg_scroll);
cmdmod = save_cmdmod;
reg_executing = save_reg_executing;
+ pending_end_reg_executing = save_pending_end_reg_executing;
if (ea.did_sandbox) {
sandbox--;
@@ -8529,6 +8531,7 @@ bool save_current_state(save_state_T *sst)
sst->save_finish_op = finish_op;
sst->save_opcount = opcount;
sst->save_reg_executing = reg_executing;
+ sst->save_pending_end_reg_executing = pending_end_reg_executing;
msg_scroll = false; // no msg scrolling in Normal mode
restart_edit = 0; // don't go to Insert mode
@@ -8559,6 +8562,7 @@ void restore_current_state(save_state_T *sst)
finish_op = sst->save_finish_op;
opcount = sst->save_opcount;
reg_executing = sst->save_reg_executing;
+ pending_end_reg_executing = sst->save_pending_end_reg_executing;
// don't reset msg_didout now
msg_didout |= sst->save_msg_didout;
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index be9f97e27d..874e0e599e 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -29,6 +29,7 @@ typedef struct {
bool save_finish_op;
long save_opcount;
int save_reg_executing;
+ bool save_pending_end_reg_executing;
tasave_T tabuf;
} save_state_T;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 9104a2dd5a..dd6e4630d2 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -6544,7 +6544,7 @@ static int open_cmdwin(void)
// win_close() may have already wiped the buffer when 'bh' is
// set to 'wipe', autocommands may have closed other windows
if (bufref_valid(&bufref) && bufref.br_buf != curbuf) {
- close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false);
+ close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false);
}
// Restore window sizes.
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index d68dda15f3..d4407b4982 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1015,27 +1015,27 @@ retry:
}
read_buf_col += n;
break;
- } else {
- // Append whole line and new-line. Change NL
- // to NUL to reverse the effect done below.
- for (ni = 0; ni < n; ni++) {
- if (p[ni] == NL) {
- ptr[tlen++] = NUL;
- } else {
- ptr[tlen++] = p[ni];
- }
+ }
+
+ // Append whole line and new-line. Change NL
+ // to NUL to reverse the effect done below.
+ for (ni = 0; ni < n; ni++) {
+ if (p[ni] == NL) {
+ ptr[tlen++] = NUL;
+ } else {
+ ptr[tlen++] = p[ni];
}
- ptr[tlen++] = NL;
- read_buf_col = 0;
- if (++read_buf_lnum > from) {
- // When the last line didn't have an
- // end-of-line don't add it now either.
- if (!curbuf->b_p_eol) {
- --tlen;
- }
- size = tlen;
- break;
+ }
+ ptr[tlen++] = NL;
+ read_buf_col = 0;
+ if (++read_buf_lnum > from) {
+ // When the last line didn't have an
+ // end-of-line don't add it now either.
+ if (!curbuf->b_p_eol) {
+ tlen--;
}
+ size = tlen;
+ break;
}
}
}
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index eddd5ccd14..299385cb17 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2060,7 +2060,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
}
/// unget one character (can only be done once!)
-/// If the character was stuffed, vgetc() will get it next time it was called.
+/// If the character was stuffed, vgetc() will get it next time it is called.
/// Otherwise vgetc() will only get it when the stuff buffer is empty.
void vungetc(int c)
{
@@ -2072,6 +2072,20 @@ void vungetc(int c)
old_KeyStuffed = KeyStuffed;
}
+/// When peeking and not getting a character, reg_executing cannot be cleared
+/// yet, so set a flag to clear it later.
+void check_end_reg_executing(bool advance)
+{
+ if (reg_executing != 0 && (typebuf.tb_maplen == 0 || pending_end_reg_executing)) {
+ if (advance) {
+ reg_executing = 0;
+ pending_end_reg_executing = false;
+ } else {
+ pending_end_reg_executing = true;
+ }
+ }
+}
+
/// Gets a byte:
/// 1. from the stuffbuffer
/// This is used for abbreviated commands like "D" -> "d$".
@@ -2126,9 +2140,7 @@ static int vgetorpeek(bool advance)
init_typebuf();
start_stuff();
- if (advance && typebuf.tb_maplen == 0) {
- reg_executing = 0;
- }
+ check_end_reg_executing(advance);
do {
// get a character: 1. from the stuffbuffer
if (typeahead_char != 0) {
@@ -2155,6 +2167,7 @@ static int vgetorpeek(bool advance)
// If a mapped key sequence is found we go back to the start to
// try re-mapping.
for (;;) {
+ check_end_reg_executing(advance);
// os_breakcheck() is slow, don't use it too often when
// inside a mapping. But call it each time for typed
// characters.
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index ace7647b35..4aa49337cf 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -643,6 +643,8 @@ EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p.
EXTERN int reg_recording INIT(= 0); // register for recording or zero
EXTERN int reg_executing INIT(= 0); // register being executed or zero
+// Flag set when peeking a character and found the end of executed register
+EXTERN bool pending_end_reg_executing INIT(= false);
EXTERN int reg_recorded INIT(= 0); // last recorded register or zero
EXTERN int no_mapping INIT(= false); // currently no mapping allowed
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 418887a6b1..5f6e1ea273 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -2327,95 +2327,88 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
* We are finished, break the loop here.
*/
break;
- } else { // pointer block full
- /*
- * split the pointer block
- * allocate a new pointer block
- * move some of the pointer into the new block
- * prepare for updating the parent block
- */
- for (;;) { // do this twice when splitting block 1
- hp_new = ml_new_ptr(mfp);
- if (hp_new == NULL) { // TODO: try to fix tree
- return FAIL;
- }
- pp_new = hp_new->bh_data;
-
- if (hp->bh_bnum != 1) {
- break;
- }
-
- /*
- * if block 1 becomes full the tree is given an extra level
- * The pointers from block 1 are moved into the new block.
- * block 1 is updated to point to the new block
- * then continue to split the new block
- */
- memmove(pp_new, pp, (size_t)page_size);
- pp->pb_count = 1;
- pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
- pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
- pp->pb_pointer[0].pe_old_lnum = 1;
- pp->pb_pointer[0].pe_page_count = 1;
- mf_put(mfp, hp, true, false); // release block 1
- hp = hp_new; // new block is to be split
- pp = pp_new;
- CHECK(stack_idx != 0, _("stack_idx should be 0"));
- ip->ip_index = 0;
- ++stack_idx; // do block 1 again later
- }
- /*
- * move the pointers after the current one to the new block
- * If there are none, the new entry will be in the new block.
- */
- total_moved = pp->pb_count - pb_idx - 1;
- if (total_moved) {
- memmove(&pp_new->pb_pointer[0],
- &pp->pb_pointer[pb_idx + 1],
- (size_t)(total_moved) * sizeof(PTR_EN));
- pp_new->pb_count = total_moved;
- pp->pb_count -= total_moved - 1;
- pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
- pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
- pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
- if (lnum_right) {
- pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
- }
- } else {
- pp_new->pb_count = 1;
- pp_new->pb_pointer[0].pe_bnum = bnum_right;
- pp_new->pb_pointer[0].pe_line_count = line_count_right;
- pp_new->pb_pointer[0].pe_page_count = page_count_right;
- pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
- }
- pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
- pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
- pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
- if (lnum_left) {
- pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
+ }
+ // pointer block full
+ //
+ // split the pointer block
+ // allocate a new pointer block
+ // move some of the pointer into the new block
+ // prepare for updating the parent block
+ for (;;) { // do this twice when splitting block 1
+ hp_new = ml_new_ptr(mfp);
+ if (hp_new == NULL) { // TODO(vim): try to fix tree
+ return FAIL;
}
- lnum_left = 0;
- lnum_right = 0;
+ pp_new = hp_new->bh_data;
- /*
- * recompute line counts
- */
- line_count_right = 0;
- for (i = 0; i < (int)pp_new->pb_count; ++i) {
- line_count_right += pp_new->pb_pointer[i].pe_line_count;
+ if (hp->bh_bnum != 1) {
+ break;
}
- line_count_left = 0;
- for (i = 0; i < (int)pp->pb_count; ++i) {
- line_count_left += pp->pb_pointer[i].pe_line_count;
+
+ // if block 1 becomes full the tree is given an extra level
+ // The pointers from block 1 are moved into the new block.
+ // block 1 is updated to point to the new block
+ // then continue to split the new block
+ memmove(pp_new, pp, (size_t)page_size);
+ pp->pb_count = 1;
+ pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
+ pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
+ pp->pb_pointer[0].pe_old_lnum = 1;
+ pp->pb_pointer[0].pe_page_count = 1;
+ mf_put(mfp, hp, true, false); // release block 1
+ hp = hp_new; // new block is to be split
+ pp = pp_new;
+ CHECK(stack_idx != 0, _("stack_idx should be 0"));
+ ip->ip_index = 0;
+ stack_idx++; // do block 1 again later
+ }
+ // move the pointers after the current one to the new block
+ // If there are none, the new entry will be in the new block.
+ total_moved = pp->pb_count - pb_idx - 1;
+ if (total_moved) {
+ memmove(&pp_new->pb_pointer[0],
+ &pp->pb_pointer[pb_idx + 1],
+ (size_t)(total_moved) * sizeof(PTR_EN));
+ pp_new->pb_count = total_moved;
+ pp->pb_count -= total_moved - 1;
+ pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
+ pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
+ pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
+ if (lnum_right) {
+ pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
}
+ } else {
+ pp_new->pb_count = 1;
+ pp_new->pb_pointer[0].pe_bnum = bnum_right;
+ pp_new->pb_pointer[0].pe_line_count = line_count_right;
+ pp_new->pb_pointer[0].pe_page_count = page_count_right;
+ pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
+ }
+ pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
+ pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
+ pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
+ if (lnum_left) {
+ pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
+ }
+ lnum_left = 0;
+ lnum_right = 0;
- bnum_left = hp->bh_bnum;
- bnum_right = hp_new->bh_bnum;
- page_count_left = 1;
- page_count_right = 1;
- mf_put(mfp, hp, true, false);
- mf_put(mfp, hp_new, true, false);
+ // recompute line counts
+ line_count_right = 0;
+ for (i = 0; i < (int)pp_new->pb_count; i++) {
+ line_count_right += pp_new->pb_pointer[i].pe_line_count;
}
+ line_count_left = 0;
+ for (i = 0; i < (int)pp->pb_count; i++) {
+ line_count_left += pp->pb_pointer[i].pe_line_count;
+ }
+
+ bnum_left = hp->bh_bnum;
+ bnum_right = hp_new->bh_bnum;
+ page_count_left = 1;
+ page_count_right = 1;
+ mf_put(mfp, hp, true, false);
+ mf_put(mfp, hp_new, true, false);
}
/*
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 6cdc4f1fde..c895cc1ec6 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -691,7 +691,7 @@ void free_all_mem(void)
bufref_T bufref;
set_bufref(&bufref, buf);
nextbuf = buf->b_next;
- close_buffer(NULL, buf, DOBUF_WIPE, false);
+ close_buffer(NULL, buf, DOBUF_WIPE, false, false);
// Didn't work, try next one.
buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 9fd0e0b222..551bc1fbdf 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5140,43 +5140,43 @@ char *set_option_value(const char *const name, const long number, const char *co
s = "";
}
return set_string_option(opt_idx, s, opt_flags);
- } else {
- varp = get_varp_scope(&(options[opt_idx]), opt_flags);
- if (varp != NULL) { // hidden option is not changed
- if (number == 0 && string != NULL) {
- int idx;
-
- // Either we are given a string or we are setting option
- // to zero.
- for (idx = 0; string[idx] == '0'; idx++) {}
- if (string[idx] != NUL || idx == 0) {
- // There's another character after zeros or the string
- // is empty. In both cases, we are trying to set a
- // num option using a string.
- semsg(_("E521: Number required: &%s = '%s'"),
- name, string);
- return NULL; // do nothing as we hit an error
- }
- }
- long numval = number;
- if (opt_flags & OPT_CLEAR) {
- if ((int *)varp == &curbuf->b_p_ar) {
- numval = -1;
- } else if ((long *)varp == &curbuf->b_p_ul) {
- numval = NO_LOCAL_UNDOLEVEL;
- } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
- numval = -1;
- } else {
- char *s = NULL;
- (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL);
- }
+ }
+
+ varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+ if (varp != NULL) { // hidden option is not changed
+ if (number == 0 && string != NULL) {
+ int idx;
+
+ // Either we are given a string or we are setting option
+ // to zero.
+ for (idx = 0; string[idx] == '0'; idx++) {}
+ if (string[idx] != NUL || idx == 0) {
+ // There's another character after zeros or the string
+ // is empty. In both cases, we are trying to set a
+ // num option using a string.
+ semsg(_("E521: Number required: &%s = '%s'"),
+ name, string);
+ return NULL; // do nothing as we hit an error
}
- if (flags & P_NUM) {
- return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
+ }
+ long numval = number;
+ if (opt_flags & OPT_CLEAR) {
+ if ((int *)varp == &curbuf->b_p_ar) {
+ numval = -1;
+ } else if ((long *)varp == &curbuf->b_p_ul) {
+ numval = NO_LOCAL_UNDOLEVEL;
+ } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
+ numval = -1;
} else {
- return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
+ char *s = NULL;
+ (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL);
}
}
+ if (flags & P_NUM) {
+ return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
+ } else {
+ return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
+ }
}
}
return NULL;
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 1d589e8ca0..4ad5e40fee 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -1721,7 +1721,7 @@ static void wipe_qf_buffer(qf_info_T *qi)
if (qfbuf != NULL && qfbuf->b_nwindows == 0) {
// If the quickfix buffer is not loaded in any window, then
// wipe the buffer.
- close_buffer(NULL, qfbuf, DOBUF_WIPE, false);
+ close_buffer(NULL, qfbuf, DOBUF_WIPE, false, false);
qi->qf_bufnr = INVALID_QFBUFNR;
}
}
@@ -5843,7 +5843,7 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start)
{
if (curbuf != buf) { // safety check
- close_buffer(NULL, buf, DOBUF_UNLOAD, false);
+ close_buffer(NULL, buf, DOBUF_UNLOAD, false, true);
// When autocommands/'autochdir' option changed directory: go back.
restore_start_dir(dirname_start);
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 3a7636085b..34e3ddf654 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -40,7 +40,7 @@ void state_enter(VimState *s)
int key;
getkey:
- // Expand mappings first by calling vpeekc() directly.
+ // Apply mappings first by calling vpeekc() directly.
// - If vpeekc() returns non-NUL, there is a character already available for processing, so
// don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence.
// - If vpeekc() returns NUL, vgetc() will block, and there are three cases:
@@ -76,6 +76,9 @@ getkey:
}
if (key == K_EVENT) {
+ // An event handler may use the value of reg_executing.
+ // Clear it if it should be cleared when getting the next character.
+ check_end_reg_executing(true);
may_sync_undo();
}
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index a82e8fa103..d884ad704b 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -2289,55 +2289,52 @@ static void check_state_ends(void)
next_match_idx = 0;
next_match_col = MAXCOL;
break;
- } else {
- // handle next_list, unless at end of line and no "skipnl" or
- // "skipempty"
- current_next_list = cur_si->si_next_list;
- current_next_flags = cur_si->si_flags;
- if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
- && syn_getcurline()[current_col] == NUL) {
- current_next_list = NULL;
- }
+ }
+
+ // handle next_list, unless at end of line and no "skipnl" or
+ // "skipempty"
+ current_next_list = cur_si->si_next_list;
+ current_next_flags = cur_si->si_flags;
+ if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
+ && syn_getcurline()[current_col] == NUL) {
+ current_next_list = NULL;
+ }
+
+ // When the ended item has "extend", another item with
+ // "keepend" now needs to check for its end.
+ had_extend = (cur_si->si_flags & HL_EXTEND);
- // When the ended item has "extend", another item with
- // "keepend" now needs to check for its end.
- had_extend = (cur_si->si_flags & HL_EXTEND);
+ pop_current_state();
- pop_current_state();
+ if (GA_EMPTY(&current_state)) {
+ break;
+ }
+ if (had_extend && keepend_level >= 0) {
+ syn_update_ends(false);
if (GA_EMPTY(&current_state)) {
break;
}
+ }
- if (had_extend && keepend_level >= 0) {
- syn_update_ends(false);
- if (GA_EMPTY(&current_state)) {
- break;
- }
- }
-
- cur_si = &CUR_STATE(current_state.ga_len - 1);
+ cur_si = &CUR_STATE(current_state.ga_len - 1);
- /*
- * Only for a region the search for the end continues after
- * the end of the contained item. If the contained match
- * included the end-of-line, break here, the region continues.
- * Don't do this when:
- * - "keepend" is used for the contained item
- * - not at the end of the line (could be end="x$"me=e-1).
- * - "excludenl" is used (HL_HAS_EOL won't be set)
- */
- if (cur_si->si_idx >= 0
- && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
- == SPTYPE_START
- && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) {
- update_si_end(cur_si, (int)current_col, true);
- check_keepend();
- if ((current_next_flags & HL_HAS_EOL)
- && keepend_level < 0
- && syn_getcurline()[current_col] == NUL) {
- break;
- }
+ // Only for a region the search for the end continues after
+ // the end of the contained item. If the contained match
+ // included the end-of-line, break here, the region continues.
+ // Don't do this when:
+ // - "keepend" is used for the contained item
+ // - not at the end of the line (could be end="x$"me=e-1).
+ // - "excludenl" is used (HL_HAS_EOL won't be set)
+ if (cur_si->si_idx >= 0
+ && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type == SPTYPE_START
+ && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) {
+ update_si_end(cur_si, (int)current_col, true);
+ check_keepend();
+ if ((current_next_flags & HL_HAS_EOL)
+ && keepend_level < 0
+ && syn_getcurline()[current_col] == NUL) {
+ break;
}
}
} else {
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 76c69ad10b..c1a120efa4 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -299,6 +299,40 @@ func Test_WinClosed()
unlet g:triggered
endfunc
+func Test_WinClosed_throws()
+ vnew
+ let bnr = bufnr()
+ call assert_equal(1, bufloaded(bnr))
+ augroup test-WinClosed
+ autocmd WinClosed * throw 'foo'
+ augroup END
+ try
+ close
+ catch /.*/
+ endtry
+ call assert_equal(0, bufloaded(bnr))
+
+ autocmd! test-WinClosed
+ augroup! test-WinClosed
+endfunc
+
+func Test_WinClosed_throws_with_tabs()
+ tabnew
+ let bnr = bufnr()
+ call assert_equal(1, bufloaded(bnr))
+ augroup test-WinClosed
+ autocmd WinClosed * throw 'foo'
+ augroup END
+ try
+ close
+ catch /.*/
+ endtry
+ call assert_equal(0, bufloaded(bnr))
+
+ autocmd! test-WinClosed
+ augroup! test-WinClosed
+endfunc
+
func s:AddAnAutocmd()
augroup vimBarTest
au BufReadCmd * echo 'hello'
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 10eb979b45..be9a77ee75 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -1295,4 +1295,41 @@ func Test_diff_filler_cursorcolumn()
endfunc
+func Test_diff_binary()
+ CheckScreendump
+
+ let content =<< trim END
+ call setline(1, ['a', 'b', "c\n", 'd', 'e', 'f', 'g'])
+ vnew
+ call setline(1, ['A', 'b', 'c', 'd', 'E', 'f', 'g'])
+ windo diffthis
+ wincmd p
+ norm! gg0
+ redraw!
+ END
+ call writefile(content, 'Xtest_diff_bin')
+ let buf = RunVimInTerminal('-S Xtest_diff_bin', {})
+
+ " Test using internal diff
+ call VerifyScreenDump(buf, 'Test_diff_bin_01', {})
+
+ " Test using internal diff and case folding
+ call term_sendkeys(buf, ":set diffopt+=icase\<cr>")
+ call term_sendkeys(buf, "\<C-l>")
+ call VerifyScreenDump(buf, 'Test_diff_bin_02', {})
+ " Test using external diff
+ call term_sendkeys(buf, ":set diffopt=filler\<cr>")
+ call term_sendkeys(buf, "\<C-l>")
+ call VerifyScreenDump(buf, 'Test_diff_bin_03', {})
+ " Test using external diff and case folding
+ call term_sendkeys(buf, ":set diffopt=filler,icase\<cr>")
+ call term_sendkeys(buf, "\<C-l>")
+ call VerifyScreenDump(buf, 'Test_diff_bin_04', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_diff_bin')
+ set diffopt&vim
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 360b3aaaa0..eea5d190b2 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1627,6 +1627,29 @@ func Test_edit_is_a_directory()
call delete(dirname, 'rf')
endfunc
+" Using :edit without leaving 'insertmode' should not cause Insert mode to be
+" re-entered immediately after <C-L>
+func Test_edit_insertmode_ex_edit()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set insertmode noruler
+ inoremap <C-B> <Cmd>edit Xfoo<CR>
+ END
+ call writefile(lines, 'Xtest_edit_insertmode_ex_edit')
+
+ let buf = RunVimInTerminal('-S Xtest_edit_insertmode_ex_edit', #{rows: 6})
+ call TermWait(buf, 50)
+ call assert_match('^-- INSERT --\s*$', term_getline(buf, 6))
+ call term_sendkeys(buf, "\<C-B>\<C-L>")
+ call TermWait(buf, 50)
+ call assert_notmatch('^-- INSERT --\s*$', term_getline(buf, 6))
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_edit_insertmode_ex_edit')
+endfunc
+
func Test_edit_browse()
" in the GUI this opens a file picker, we only test the terminal behavior
CheckNotGui
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index a31ff50a48..42271a014d 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -395,7 +395,7 @@ let s:filename_checks = {
\ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'],
\ 'pf': ['pf.conf'],
\ 'pfmain': ['main.cf'],
- \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp'],
+ \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt'],
\ 'lpc': ['file.lpc', 'file.ulpc'],
\ 'pike': ['file.pike', 'file.pmod'],
\ 'cmod': ['file.cmod'],
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index 5588e515e1..efdf44a0d6 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -615,6 +615,14 @@ func Test_cursorcolumn_insert_on_tab()
call TermWait(buf)
call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {})
+ call term_sendkeys(buf, "\<C-O>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_3', {})
+
+ call term_sendkeys(buf, 'i')
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {})
+
call StopVimInTerminal(buf)
call delete('Xcuc_insert_on_tab')
endfunc
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 186fa8871f..24eaf9e8b1 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -707,4 +707,23 @@ func Test_z1_complete_no_history()
close!
endfunc
+func FooBarComplete(findstart, base)
+ if a:findstart
+ return col('.') - 1
+ else
+ return ["Foo", "Bar", "}"]
+ endif
+endfunc
+
+func Test_complete_smartindent()
+ new
+ setlocal smartindent completefunc=FooBarComplete
+
+ exe "norm! o{\<cr>\<c-x>\<c-u>\<c-p>}\<cr>\<esc>"
+ let result = getline(1,'$')
+ call assert_equal(['', '{','}',''], result)
+ bw!
+ delfunction! FooBarComplete
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index f78b748d71..c623edd5a1 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -690,5 +690,23 @@ func Test_record_in_select_mode()
bwipe!
endfunc
+func Test_end_reg_executing()
+ nnoremap s <Nop>
+ let @a = 's'
+ call feedkeys("@aqaq\<Esc>", 'tx')
+ call assert_equal('', @a)
+ call assert_equal('', getline(1))
+
+ call setline(1, 'aaa')
+ nnoremap s qa
+ let @a = 'fa'
+ call feedkeys("@asq\<Esc>", 'tx')
+ call assert_equal('', @a)
+ call assert_equal('aaa', getline(1))
+
+ nunmap s
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index 7e513180a3..adc1745b39 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -1972,6 +1972,29 @@ func Test_builtin_func_error()
call assert_equal('jlmnpqrtueghivyzACD', g:Xpath)
endfunc
-" Modelines {{{1
+func Test_reload_in_try_catch()
+ call writefile(['x'], 'Xreload')
+ set autoread
+ edit Xreload
+ tabnew
+ call writefile(['xx'], 'Xreload')
+ augroup ReLoad
+ au FileReadPost Xreload let x = doesnotexist
+ au BufReadPost Xreload let x = doesnotexist
+ augroup END
+ try
+ edit Xreload
+ catch
+ endtry
+ tabnew
+
+ tabclose
+ tabclose
+ autocmd! ReLoad
+ set noautoread
+ bwipe! Xreload
+ call delete('Xreload')
+endfunc
+
+" Modeline {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
-"-------------------------------------------------------------------------------
diff --git a/src/nvim/window.c b/src/nvim/window.c
index cc21bf25b0..9aa2f947cb 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2541,6 +2541,41 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev
return true;
}
+/// Close the buffer of "win" and unload it if "free_buf" is true.
+/// "abort_if_last" is passed to close_buffer(): abort closing if all other
+/// windows are closed.
+static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last)
+{
+ // Free independent synblock before the buffer is freed.
+ if (win->w_buffer != NULL) {
+ reset_synblock(win);
+ }
+
+ // When a quickfix/location list window is closed and the buffer is
+ // displayed in only one window, then unlist the buffer.
+ if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)
+ && win->w_buffer->b_nwindows == 1) {
+ win->w_buffer->b_p_bl = false;
+ }
+
+ // Close the link to the buffer.
+ if (win->w_buffer != NULL) {
+ bufref_T bufref;
+ set_bufref(&bufref, curbuf);
+ win->w_closing = true;
+ close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last, true);
+ if (win_valid_any_tab(win)) {
+ win->w_closing = false;
+ }
+
+ // Make sure curbuf is valid. It can become invalid if 'bufhidden' is
+ // "wipe".
+ if (!bufref_valid(&bufref)) {
+ curbuf = firstbuf;
+ }
+ }
+}
+
// Close window "win". Only works for the current tab page.
// If "free_buf" is true related buffer may be unloaded.
//
@@ -2679,36 +2714,7 @@ int win_close(win_T *win, bool free_buf, bool force)
return OK;
}
- // Free independent synblock before the buffer is freed.
- if (win->w_buffer != NULL) {
- reset_synblock(win);
- }
-
- // When a quickfix/location list window is closed and the buffer is
- // displayed in only one window, then unlist the buffer.
- if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)
- && win->w_buffer->b_nwindows == 1) {
- win->w_buffer->b_p_bl = false;
- }
-
- /*
- * Close the link to the buffer.
- */
- if (win->w_buffer != NULL) {
- bufref_T bufref;
- set_bufref(&bufref, curbuf);
- win->w_closing = true;
- close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true);
- if (win_valid_any_tab(win)) {
- win->w_closing = false;
- }
-
- // Make sure curbuf is valid. It can become invalid if 'bufhidden' is
- // "wipe".
- if (!bufref_valid(&bufref)) {
- curbuf = firstbuf;
- }
- }
+ win_close_buffer(win, free_buf, true);
if (only_one_window() && win_valid(win) && win->w_buffer == NULL
&& (last_window(win) || curtab != prev_curtab
@@ -2879,7 +2885,7 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
if (win->w_buffer != NULL) {
// Close the link to the buffer.
- close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false);
+ close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, true);
}
// Careful: Autocommands may have closed the tab page or made it the
diff --git a/test/functional/editor/macro_spec.lua b/test/functional/editor/macro_spec.lua
index c0c9256af2..d4cf6b28fd 100644
--- a/test/functional/editor/macro_spec.lua
+++ b/test/functional/editor/macro_spec.lua
@@ -6,11 +6,14 @@ local feed = helpers.feed
local clear = helpers.clear
local expect = helpers.expect
local command = helpers.command
+local funcs = helpers.funcs
+local meths = helpers.meths
local insert = helpers.insert
local curbufmeths = helpers.curbufmeths
+before_each(clear)
+
describe('macros', function()
- before_each(clear)
it('can be recorded and replayed', function()
feed('qiahello<esc>q')
expect('hello')
@@ -47,9 +50,47 @@ hello]]
end)
end)
-describe('reg_recorded()', function()
- before_each(clear)
+describe('immediately after a macro has finished executing,', function()
+ before_each(function()
+ command([[let @a = 'gg0']])
+ end)
+
+ describe('reg_executing() from RPC returns an empty string', function()
+ it('if the macro does not end with a <Nop> mapping', function()
+ feed('@a')
+ eq('', funcs.reg_executing())
+ end)
+
+ it('if the macro ends with a <Nop> mapping', function()
+ command('nnoremap 0 <Nop>')
+ feed('@a')
+ eq('', funcs.reg_executing())
+ end)
+ end)
+ describe('characters from a mapping are not treated as a part of the macro #18015', function()
+ before_each(function()
+ command('nnoremap s qa')
+ end)
+
+ it('if the macro does not end with a <Nop> mapping', function()
+ feed('@asq') -- "q" from "s" mapping should start recording a macro instead of being no-op
+ eq({mode = 'n', blocking = false}, meths.get_mode())
+ expect('')
+ eq('', eval('@a'))
+ end)
+
+ it('if the macro ends with a <Nop> mapping', function()
+ command('nnoremap 0 <Nop>')
+ feed('@asq') -- "q" from "s" mapping should start recording a macro instead of being no-op
+ eq({mode = 'n', blocking = false}, meths.get_mode())
+ expect('')
+ eq('', eval('@a'))
+ end)
+ end)
+end)
+
+describe('reg_recorded()', function()
it('returns the correct value', function()
feed [[qqyyq]]
eq('q', eval('reg_recorded()'))
diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua
index 528e228121..c38acbe96a 100644
--- a/test/functional/editor/mode_insert_spec.lua
+++ b/test/functional/editor/mode_insert_spec.lua
@@ -6,6 +6,8 @@ local expect = helpers.expect
local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
+local meths = helpers.meths
+local poke_eventloop = helpers.poke_eventloop
describe('insert-mode', function()
before_each(function()
@@ -128,4 +130,26 @@ describe('insert-mode', function()
expect('<M-1><D-2><M-7><D-8>')
end)
end)
+
+ describe([[With 'insertmode', Insert mode is not re-entered immediately after <C-L>]], function()
+ before_each(function()
+ command('set insertmode')
+ poke_eventloop()
+ eq({mode = 'i', blocking = false}, meths.get_mode())
+ end)
+
+ it('after calling :edit from <Cmd> mapping', function()
+ command('inoremap <C-B> <Cmd>edit Xfoo<CR>')
+ feed('<C-B><C-L>')
+ poke_eventloop()
+ eq({mode = 'n', blocking = false}, meths.get_mode())
+ end)
+
+ it('after calling :edit from RPC #16823', function()
+ command('edit Xfoo')
+ feed('<C-L>')
+ poke_eventloop()
+ eq({mode = 'n', blocking = false}, meths.get_mode())
+ end)
+ end)
end)
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
index 3a25d7e813..6f67dea2be 100644
--- a/test/functional/ui/diff_spec.lua
+++ b/test/functional/ui/diff_spec.lua
@@ -1226,3 +1226,130 @@ it('Align the filler lines when changing text in diff mode', function()
|
]]}
end)
+
+it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', function()
+ clear()
+ local screen = Screen.new(40, 20)
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray};
+ [2] = {reverse = true};
+ [3] = {background = Screen.colors.LightBlue};
+ [4] = {background = Screen.colors.LightMagenta};
+ [5] = {background = Screen.colors.Red, bold = true};
+ [6] = {foreground = Screen.colors.Blue, bold = true};
+ [7] = {background = Screen.colors.Red, foreground = Screen.colors.Blue, bold = true};
+ [8] = {reverse = true, bold = true};
+ })
+ screen:attach()
+ exec([[
+ call setline(1, ['a', 'b', "c\n", 'd', 'e', 'f', 'g'])
+ vnew
+ call setline(1, ['A', 'b', 'c', 'd', 'E', 'f', 'g'])
+ windo diffthis
+ wincmd p
+ norm! gg0
+ redraw!
+ ]])
+
+ -- Test using internal diff
+ screen:expect([[
+ {1: }{5:^A}{4: }{2:│}{1: }{5:a}{4: }|
+ {1: }b {2:│}{1: }b |
+ {1: }{4:c }{2:│}{1: }{4:c}{7:^@}{4: }|
+ {1: }d {2:│}{1: }d |
+ {1: }{5:E}{4: }{2:│}{1: }{5:e}{4: }|
+ {1: }f {2:│}{1: }f |
+ {1: }g {2:│}{1: }g |
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {8:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+
+ -- Test using internal diff and case folding
+ command('set diffopt+=icase')
+ feed('<C-L>')
+ screen:expect([[
+ {1: }^A {2:│}{1: }a |
+ {1: }b {2:│}{1: }b |
+ {1: }{4:c }{2:│}{1: }{4:c}{7:^@}{4: }|
+ {1: }d {2:│}{1: }d |
+ {1: }E {2:│}{1: }e |
+ {1: }f {2:│}{1: }f |
+ {1: }g {2:│}{1: }g |
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {8:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+
+ -- Test using external diff
+ command('set diffopt=filler')
+ feed('<C-L>')
+ screen:expect([[
+ {1: }{5:^A}{4: }{2:│}{1: }{5:a}{4: }|
+ {1: }b {2:│}{1: }b |
+ {1: }{4:c }{2:│}{1: }{4:c}{7:^@}{4: }|
+ {1: }d {2:│}{1: }d |
+ {1: }{5:E}{4: }{2:│}{1: }{5:e}{4: }|
+ {1: }f {2:│}{1: }f |
+ {1: }g {2:│}{1: }g |
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {8:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+
+ -- Test using external diff and case folding
+ command('set diffopt+=filler,icase')
+ feed('<C-L>')
+ screen:expect([[
+ {1: }^A {2:│}{1: }a |
+ {1: }b {2:│}{1: }b |
+ {1: }{4:c }{2:│}{1: }{4:c}{7:^@}{4: }|
+ {1: }d {2:│}{1: }d |
+ {1: }E {2:│}{1: }e |
+ {1: }f {2:│}{1: }f |
+ {1: }g {2:│}{1: }g |
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {6:~ }{2:│}{6:~ }|
+ {8:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+end)
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 4b63b09a5b..8afc69a649 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -1345,6 +1345,28 @@ describe('CursorColumn highlight', function()
{2:~ }|
{3:-- INSERT --} |
]])
+ feed('<C-O>')
+ screen:expect([[
+ 1234567{1:8}9 |
+ a ^ b |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:-- (insert) --} |
+ ]])
+ feed('i')
+ screen:expect([[
+ 1{1:2}3456789 |
+ a^ b |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:-- INSERT --} |
+ ]])
end)
it('is updated if cursor is moved from timer', function()
diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua
index 34c88248e6..833b21b00b 100644
--- a/test/unit/buffer_spec.lua
+++ b/test/unit/buffer_spec.lua
@@ -17,8 +17,8 @@ describe('buffer functions', function()
return buffer.buflist_new(c_file, c_file, 1, flags)
end
- local close_buffer = function(win, buf, action, abort_if_last)
- return buffer.close_buffer(win, buf, action, abort_if_last)
+ local close_buffer = function(win, buf, action, abort_if_last, ignore_abort)
+ return buffer.close_buffer(win, buf, action, abort_if_last, ignore_abort)
end
local path1 = 'test_file_path'
@@ -53,7 +53,7 @@ describe('buffer functions', function()
itp('should view a closed and hidden buffer as valid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
- close_buffer(NULL, buf, 0, 0)
+ close_buffer(NULL, buf, 0, 0, 0)
eq(true, buffer.buf_valid(buf))
end)
@@ -61,7 +61,7 @@ describe('buffer functions', function()
itp('should view a closed and unloaded buffer as valid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
- close_buffer(NULL, buf, buffer.DOBUF_UNLOAD, 0)
+ close_buffer(NULL, buf, buffer.DOBUF_UNLOAD, 0, 0)
eq(true, buffer.buf_valid(buf))
end)
@@ -69,7 +69,7 @@ describe('buffer functions', function()
itp('should view a closed and wiped buffer as invalid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
- close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0, 0)
eq(false, buffer.buf_valid(buf))
end)
@@ -90,7 +90,7 @@ describe('buffer functions', function()
eq(buf.handle, buflist_findpat(path1, ONLY_LISTED))
- close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0, 0)
end)
itp('should prefer to match the start of a file path', function()
@@ -102,9 +102,9 @@ describe('buffer functions', function()
eq(buf2.handle, buflist_findpat("file", ONLY_LISTED))
eq(buf3.handle, buflist_findpat("path", ONLY_LISTED))
- close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
end)
itp('should prefer to match the end of a file over the middle', function()
@@ -118,7 +118,7 @@ describe('buffer functions', function()
--}
--{ When: We close buf2
- close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
-- And: Open buf1, which has 'file' in the middle of its name
local buf1 = buflist_new(path1, buffer.BLN_LISTED)
@@ -127,8 +127,8 @@ describe('buffer functions', function()
eq(buf3.handle, buflist_findpat("file", ONLY_LISTED))
--}
- close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
end)
itp('should match a unique fragment of a file path', function()
@@ -138,9 +138,9 @@ describe('buffer functions', function()
eq(buf3.handle, buflist_findpat("_test_", ONLY_LISTED))
- close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
end)
itp('should include / ignore unlisted buffers based on the flag.', function()
@@ -152,7 +152,7 @@ describe('buffer functions', function()
--}
--{ When: We unlist the buffer
- close_buffer(NULL, buf3, buffer.DOBUF_DEL, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_DEL, 0, 0)
-- Then: It should not find the buffer when searching only listed buffers
eq(-1, buflist_findpat("_test_", ONLY_LISTED))
@@ -162,7 +162,7 @@ describe('buffer functions', function()
--}
--{ When: We wipe the buffer
- close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
-- Then: It should not find the buffer at all
eq(-1, buflist_findpat("_test_", ONLY_LISTED))
@@ -180,7 +180,7 @@ describe('buffer functions', function()
--}
--{ When: The first buffer is unlisted
- close_buffer(NULL, buf1, buffer.DOBUF_DEL, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_DEL, 0, 0)
-- Then: The second buffer is preferred because
-- unlisted buffers are not allowed
@@ -194,7 +194,7 @@ describe('buffer functions', function()
--}
--{ When: We unlist the second buffer
- close_buffer(NULL, buf2, buffer.DOBUF_DEL, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_DEL, 0, 0)
-- Then: The first buffer is preferred again
-- because buf1 matches better which takes precedence
@@ -205,8 +205,8 @@ describe('buffer functions', function()
eq(-1, buflist_findpat("test", ONLY_LISTED))
--}
- close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
end)
end)