aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAIN.md30
-rw-r--r--cmake.deps/CMakeLists.txt4
-rw-r--r--runtime/doc/index.txt4
-rw-r--r--runtime/doc/lua.txt13
-rw-r--r--runtime/doc/pi_health.txt6
-rw-r--r--runtime/doc/quickref.txt2
-rw-r--r--runtime/doc/tagsrch.txt6
-rw-r--r--runtime/lua/vim/shared.lua16
-rw-r--r--runtime/queries/help/highlights.scm4
-rw-r--r--scripts/lintcommit.lua21
-rwxr-xr-xscripts/vim-patch.sh59
-rw-r--r--src/nvim/api/vim.c2
-rw-r--r--src/nvim/buffer.h3
-rw-r--r--src/nvim/cmdexpand.c2
-rw-r--r--src/nvim/diff.c709
-rw-r--r--src/nvim/drawscreen.c52
-rw-r--r--src/nvim/eval/encode.h8
-rw-r--r--src/nvim/eval/userfunc.c58
-rw-r--r--src/nvim/ex_cmds_defs.h2
-rw-r--r--src/nvim/testdir/test_arglist.vim2
-rw-r--r--src/nvim/testdir/test_langmap.vim2
-rw-r--r--src/nvim/testdir/test_menu.vim2
-rw-r--r--src/nvim/testdir/test_quickfix.vim2
-rw-r--r--src/nvim/testdir/test_timers.vim25
-rw-r--r--src/nvim/testdir/test_vartabs.vim2
-rw-r--r--src/nvim/testdir/test_visual.vim2
-rw-r--r--src/nvim/viml/parser/expressions.h2
-rw-r--r--src/nvim/viml/parser/parser.h13
-rw-r--r--test/functional/lua/vim_spec.lua26
-rw-r--r--test/functional/ui/decorations_spec.lua4
-rw-r--r--test/functional/ui/screen_basic_spec.lua25
-rw-r--r--test/functional/vimscript/eval_spec.lua37
32 files changed, 657 insertions, 488 deletions
diff --git a/MAINTAIN.md b/MAINTAIN.md
index d65337f92b..a587425f57 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -98,6 +98,36 @@ These dependencies are "vendored" (inlined), we must update the sources manually
We may maintain forks, if we are waiting on upstream changes: https://github.com/neovim/neovim/wiki/Deps
+CI
+--------------
+
+### General
+
+As our CI is primarily dependent on GitHub Actions at the moment, then so will
+our CI strategy be. The following guidelines have worked well for us so far:
+
+* Never use a macOS runner if an Ubuntu or a Windows runner can be used
+ instead. This is because macOS runners have a [tighter restrictions on the
+ number of concurrent jobs](https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits).
+
+### Runner versions
+
+* For special-purpose jobs where the runner version doesn't really matter,
+ prefer `-latest` tags so we don't need to manually bump the versions. An
+ example of a special-purpose workflow is `labeler.yml`.
+
+* For our testing jobs, which is currently only `ci.yml`, prefer to use the
+ latest stable (i.e. non-beta) version explicitly. Avoid using the `-latest`
+ tags here as it makes it difficult to determine from an unrelated PR if a
+ failure is due to the PR itself or due to GitHub bumping the `-latest` tag
+ without our knowledge. There's also a high risk that automatic bumping the CI
+ versions will fail due to manual work being required from experience.
+
+* For our release job, which is `release.yml`, prefer to use the oldest stable
+ (i.e. non-deprecated) versions available. The reason is that we're trying to
+ produce images that work in the broadest number of environments, and
+ therefore want to use older releases.
+
See also
--------
diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt
index 7588291fbf..54a70aaa65 100644
--- a/cmake.deps/CMakeLists.txt
+++ b/cmake.deps/CMakeLists.txt
@@ -203,8 +203,8 @@ set(TREESITTER_LUA_SHA256 564594fe0ffd2f2fb3578a15019b723e1bc94ac82cb6a0103a6b3b
set(TREESITTER_VIM_URL https://github.com/vigoux/tree-sitter-viml/archive/v0.2.0.tar.gz)
set(TREESITTER_VIM_SHA256 608dcc31a7948cb66ae7f45494620e2e9face1af75598205541f80d782ec4501)
-set(TREESITTER_HELP_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v1.2.3.tar.gz)
-set(TREESITTER_HELP_SHA256 e0a0d1c6c142b0096bb9ca22acd7e7ad3b205fbe68c26e6bfbbe03632c04db08)
+set(TREESITTER_HELP_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v1.2.4.tar.gz)
+set(TREESITTER_HELP_SHA256 e1595148092c082f6d50989a0f096182b39fd684449d09c4975ed403bfa42f10)
set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.7.tar.gz)
set(TREESITTER_SHA256 b355e968ec2d0241bbd96748e00a9038f83968f85d822ecb9940cbe4c42e182e)
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 9eed5a8966..376487ad1d 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1208,8 +1208,8 @@ tag command action ~
|:cgetfile| :cg[etfile] read file with error messages
|:changes| :changes print the change list
|:chdir| :chd[ir] change directory
-|:checkhealth| :checkh[ealth] run healthchecks
-|:checkpath| :che[ckpath] list included files
+|:checkhealth| :che[ckhealth] run healthchecks
+|:checkpath| :checkp[ath] list included files
|:checktime| :checkt[ime] check timestamp of loaded buffers
|:chistory| :chi[story] list the error lists
|:clast| :cla[st] go to the specified error, default last one
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index ce16c208cd..70a28a7519 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1573,7 +1573,7 @@ gsplit({s}, {sep}, {plain}) *vim.gsplit()*
Parameters: ~
• {s} (string) String to split
• {sep} (string) Separator or pattern
- • {plain} (boolean) If `true` use `sep` literally (passed to
+ • {plain} (boolean|nil) If `true` use `sep` literally (passed to
string.find)
Return: ~
@@ -1645,15 +1645,11 @@ split({s}, {sep}, {kwargs}) *vim.split()*
split("|x|y|z|", "|", {trimempty=true}) => {'x', 'y', 'z'}
<
- @alias split_kwargs {plain: boolean, trimempty: boolean} | boolean | nil
-
- See also: ~
- |vim.gsplit()|
-
Parameters: ~
• {s} (string) String to split
• {sep} (string) Separator or pattern
- • {kwargs} (table|nil) Keyword arguments:
+ • {kwargs} ({plain: boolean, trimempty: boolean}|nil) Keyword
+ arguments:
• plain: (boolean) If `true` use `sep` literally (passed to
string.find)
• trimempty: (boolean) If `true` remove empty items from the
@@ -1662,6 +1658,9 @@ split({s}, {sep}, {kwargs}) *vim.split()*
Return: ~
string[] List of split components
+ See also: ~
+ |vim.gsplit()|
+
startswith({s}, {prefix}) *vim.startswith()*
Tests if `s` starts with `prefix`.
diff --git a/runtime/doc/pi_health.txt b/runtime/doc/pi_health.txt
index 8a6437fdc8..5ba5d1beef 100644
--- a/runtime/doc/pi_health.txt
+++ b/runtime/doc/pi_health.txt
@@ -21,8 +21,8 @@ Plugin authors are encouraged to write new healthchecks. |health-dev|
==============================================================================
Commands *health-commands*
- *:checkhealth* *:CheckHealth*
-:checkhealth Run all healthchecks.
+ *:che* *:checkhealth* *:CheckHealth*
+:che[ckhealth] Run all healthchecks.
*E5009*
Nvim depends on |$VIMRUNTIME|, 'runtimepath' and 'packpath' to
find the standard "runtime files" for syntax highlighting,
@@ -30,7 +30,7 @@ Commands *health-commands*
:checkhealth). If the runtime files cannot be found then
those features will not work.
-:checkhealth {plugins}
+:che[ckhealth] {plugins}
Run healthcheck(s) for one or more plugins. E.g. to run only
the standard Nvim healthcheck: >
:checkhealth nvim
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index f1294a18ba..2f91498857 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -827,7 +827,7 @@ Short explanation of each option: *option-list*
'printmbcharset' 'pmbcs' CJK character set to be used for :hardcopy
'printmbfont' 'pmbfn' font names to be used for CJK output of :hardcopy
'printoptions' 'popt' controls the format of :hardcopy output
-'pumheight' 'ph' maximum height of the popup menu
+'pumheight' 'ph' maximum number of items to show in the popup menu
'pumwidth' 'pw' minimum width of the popup menu
'pyxversion' 'pyx' Python version used for pyx* commands
'quoteescape' 'qe' escape characters used in a string
diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt
index 143930c48a..d079db0717 100644
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -829,10 +829,10 @@ CTRL-W d Open a new window, with the cursor on the first
(default: whole file).
See |:search-args| for [/] and [!].
- *:che* *:chec* *:check* *:checkpath*
-:che[ckpath] List all the included files that could not be found.
+ *:checkp* *:checkpath*
+:checkp[ath] List all the included files that could not be found.
-:che[ckpath]! List all the included files.
+:checkp[ath]! List all the included files.
*:search-args*
Common arguments for the commands above:
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index f03d608e56..f980547ae4 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -62,7 +62,7 @@ end)()
---
---@param s string String to split
---@param sep string Separator or pattern
----@param plain boolean If `true` use `sep` literally (passed to string.find)
+---@param plain (boolean|nil) If `true` use `sep` literally (passed to string.find)
---@return fun():string (function) Iterator over the split components
function vim.gsplit(s, sep, plain)
vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } })
@@ -108,11 +108,9 @@ end
---
---@see |vim.gsplit()|
---
----@alias split_kwargs {plain: boolean, trimempty: boolean} | boolean | nil
----
---@param s string String to split
---@param sep string Separator or pattern
----@param kwargs? {plain: boolean, trimempty: boolean} (table|nil) Keyword arguments:
+---@param kwargs ({plain: boolean, trimempty: boolean}|nil) Keyword arguments:
--- - plain: (boolean) If `true` use `sep` literally (passed to string.find)
--- - trimempty: (boolean) If `true` remove empty items from the front
--- and back of the list
@@ -159,8 +157,8 @@ end
---
---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
---
----@param t table<T, any> (table) Table
---@generic T: table
+---@param t table<T, any> (table) Table
---@return T[] (list) List of keys
function vim.tbl_keys(t)
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
@@ -417,8 +415,8 @@ end
---@generic T: table
---@param dst T List which will be modified and appended to
---@param src table List from which values will be inserted
----@param start? number Start index on src. Defaults to 1
----@param finish? number Final index on src. Defaults to `#src`
+---@param start (number|nil) Start index on src. Defaults to 1
+---@param finish (number|nil) Final index on src. Defaults to `#src`
---@return T dst
function vim.list_extend(dst, src, start, finish)
vim.validate({
@@ -486,7 +484,7 @@ function vim.tbl_islist(t)
-- TODO(bfredl): in the future, we will always be inside nvim
-- then this check can be deleted.
if vim._empty_dict_mt == nil then
- return nil
+ return false
end
return getmetatable(t) ~= vim._empty_dict_mt
end
@@ -544,7 +542,7 @@ end
---@return string %-escaped pattern string
function vim.pesc(s)
vim.validate({ s = { s, 's' } })
- return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
+ return (s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1'))
end
--- Tests if `s` starts with `prefix`.
diff --git a/runtime/queries/help/highlights.scm b/runtime/queries/help/highlights.scm
index cd4b6f8269..50beeed8b9 100644
--- a/runtime/queries/help/highlights.scm
+++ b/runtime/queries/help/highlights.scm
@@ -14,6 +14,10 @@
"`" @conceal (#set! conceal "")
text: (_) @text.literal)
(codeblock) @text.literal
+(codeblock
+ ">" @conceal (#set! conceal ""))
+(block
+ "<" @conceal (#set! conceal ""))
(argument) @parameter
(keycode) @string.special
(url) @text.uri
diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua
index 16326cfe66..87a8e62503 100644
--- a/scripts/lintcommit.lua
+++ b/scripts/lintcommit.lua
@@ -86,11 +86,28 @@ local function validate_commit(commit_message)
vim.inspect(allowed_types))
end
- -- Check if scope is empty
+ -- Check if scope is appropriate
if before_colon:match("%(") then
local scope = vim.trim(before_colon:match("%((.*)%)"))
+
if scope == '' then
- return [[Scope can't be empty.]]
+ return [[Scope can't be empty]]
+ end
+
+ if vim.startswith(scope, "nvim_") then
+ return [[Scope should be "api" instead of "nvim_..."]]
+ end
+
+ local alternative_scope = {
+ ['filetype.vim'] = 'filetype',
+ ['filetype.lua'] = 'filetype',
+ ['tree-sitter'] = 'treesitter',
+ ['ts'] = 'treesitter',
+ ['hl'] = 'highlight',
+ }
+
+ if alternative_scope[scope] then
+ return ('Scope should be "%s" instead of "%s"'):format(alternative_scope[scope], scope)
end
end
diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh
index be7c6fd8a6..0a2b1df197 100755
--- a/scripts/vim-patch.sh
+++ b/scripts/vim-patch.sh
@@ -120,9 +120,9 @@ get_vim_sources() {
commit_message() {
if [[ -n "$vim_tag" ]]; then
- printf '%s\n%s' "${vim_message}" "${vim_commit_url}"
+ printf '%s\n\n%s\n\n%s' "${vim_message}" "${vim_commit_url}" "${vim_coauthor}"
else
- printf 'vim-patch:%s\n\n%s\n%s' "$vim_version" "$vim_message" "$vim_commit_url"
+ printf 'vim-patch:%s\n\n%s\n\n%s\n\n%s' "$vim_version" "$vim_message" "$vim_commit_url" "$vim_coauthor"
fi
}
@@ -175,6 +175,7 @@ assign_commit_details() {
vim_commit_url="https://github.com/vim/vim/commit/${vim_commit}"
vim_message="$(git -C "${VIM_SOURCE_DIR}" log -1 --pretty='format:%B' "${vim_commit}" \
| sed -e 's/\(#[0-9]\{1,\}\)/vim\/vim\1/g')"
+ vim_coauthor="$(git -C "${VIM_SOURCE_DIR}" log -1 --pretty='format:Co-authored-by: %an <%ae>' "${vim_commit}")"
if [[ ${munge_commit_line} == "true" ]]; then
# Remove first line of commit message.
vim_message="$(echo "${vim_message}" | sed -e '1s/^patch /vim-patch:/')"
@@ -278,6 +279,53 @@ preprocess_patch() {
"$file" > "$file".tmp && mv "$file".tmp "$file"
}
+uncrustify_patch() {
+ local commit="$1"
+ local changed_files=()
+ while IFS='' read -r file; do changed_files+=("$file"); done < <(git diff-tree --name-only --no-commit-id -r "${commit}")
+
+ local patch_path=$NVIM_SOURCE_DIR/build/vim_patch
+ rm -rf "$patch_path"
+ mkdir -p "$patch_path"/{before,after,patch}
+
+ git checkout --quiet "$commit"~
+ for file in "${changed_files[@]}"; do
+ if [[ -e $file ]]; then
+ cp "$file" "$patch_path"/before
+ fi
+ done
+
+ git checkout --quiet "$commit"
+ for file in "${changed_files[@]}"; do
+ if [[ -e $file ]]; then
+ cp "$file" "$patch_path"/after
+ fi
+ done
+
+ # If the difference are drastic enough uncrustify may need to be used more
+ # than once. This is obviously a bug that needs to be fixed on uncrustify's
+ # end, but in the meantime this workaround is sufficient.
+ for _ in {1..2}; do
+ uncrustify -c "$NVIM_SOURCE_DIR"/src/uncrustify.cfg -q --replace --no-backup "$patch_path"/{before,after}/*.[ch]
+ done
+
+ for file in "${changed_files[@]}"; do
+ local basename
+ basename=$(basename "$file")
+ local before=$patch_path/before/$basename
+ local after=$patch_path/after/$basename
+ local patchfile="$patch_path"/patch/"$basename".patch
+ if [[ ! -e $before ]] || [[ ! -e $after ]]; then
+ continue
+ fi
+ git --no-pager diff --no-index --patch --unified=5 --color=never "$before" "$after" > "$patchfile"
+ sed -E "s|$before|/$file|g" -i "$patchfile"
+ sed -E "s|$after|/$file|g" -i "$patchfile"
+ done
+
+ cat "$patch_path"/patch/*.patch
+}
+
get_vimpatch() {
get_vim_sources
@@ -286,7 +334,12 @@ get_vimpatch() {
msg_ok "Found Vim revision '${vim_commit}'."
local patch_content
- patch_content="$(git --no-pager show --unified=5 --color=never -1 --pretty=medium "${vim_commit}")"
+ if check_executable uncrustify; then
+ patch_content="$(uncrustify_patch "${vim_commit}")"
+ git switch --quiet master
+ else
+ patch_content="$(git --no-pager show --unified=5 --color=never -1 --pretty=medium "${vim_commit}")"
+ fi
cd "${NVIM_SOURCE_DIR}"
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 32d52bef46..beb48b8d7d 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1685,7 +1685,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *er
// error handled after loop
break;
}
- // TODO(bfredl): wastefull copy. It could be avoided to encoding to msgpack
+ // TODO(bfredl): wasteful copy. It could be avoided to encoding to msgpack
// directly here. But `result` might become invalid when next api function
// is called in the loop.
ADD_C(results, copy_object(result, arena));
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index ae3d6cc117..7380bb045a 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -69,8 +69,7 @@ EXTERN char *msg_qflist INIT(= N_("[Quickfix List]"));
# include "buffer.h.generated.h"
#endif
-static inline void buf_set_changedtick(buf_T *buf,
- varnumber_T changedtick)
+static inline void buf_set_changedtick(buf_T *buf, varnumber_T changedtick)
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
/// Set b:changedtick, also checking b: for consistency in debug build
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 47027ae1e7..ca95a9a24e 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -2351,7 +2351,7 @@ static void expand_shellcmd(char *filepat, int *num_file, char ***file, int flag
}
// Go over all directories in $PATH. Expand matches in that directory and
- // collect them in "ga". When "." is not in $PATH also expaned for the
+ // collect them in "ga". When "." is not in $PATH also expand for the
// current directory, to find "subdir/cmd".
ga_init(&ga, (int)sizeof(char *), 10);
hashtab_T found_ht;
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 9446d35630..87ead61c36 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -26,6 +26,7 @@
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/garray.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -74,13 +75,13 @@ static TriState diff_a_works = kNone;
// used for diff input
typedef struct {
- char_u *din_fname; // used for external diff
+ char *din_fname; // used for external diff
mmfile_t din_mmfile; // used for internal diff
} diffin_T;
// used for diff result
typedef struct {
- char_u *dout_fname; // used for external diff
+ char *dout_fname; // used for external diff
garray_T dout_ga; // used for internal diff
} diffout_T;
@@ -100,6 +101,12 @@ typedef struct {
int dio_internal; // using internal diff
} diffio_T;
+typedef enum {
+ DIFF_ED,
+ DIFF_UNIFIED,
+ DIFF_NONE,
+} diffstyle_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "diff.c.generated.h"
#endif
@@ -204,13 +211,7 @@ static void diff_buf_clear(void)
/// @return Its index or DB_COUNT if not found.
static int diff_buf_idx(buf_T *buf)
{
- int idx;
- for (idx = 0; idx < DB_COUNT; idx++) {
- if (curtab->tp_diffbuf[idx] == buf) {
- break;
- }
- }
- return idx;
+ return diff_buf_idx_tp(buf, curtab);
}
/// Find buffer "buf" in the list of diff buffers for tab page "tp".
@@ -309,8 +310,6 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
diff_T *dp = tp->tp_first_diff;
linenr_T lnum_deleted = line1; // lnum of remaining deletion
- linenr_T n;
- linenr_T off;
for (;;) {
// If the change is after the previous diff block and before the next
// diff block, thus not touching an existing change, create a new diff
@@ -374,7 +373,8 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
// 2. 3. 4. 5.: inserted/deleted lines touching this diff.
if (deleted > 0) {
- off = 0;
+ linenr_T n;
+ linenr_T off = 0;
if (dp->df_lnum[idx] >= line1) {
if (last <= line2) {
// 4. delete all lines of diff
@@ -704,16 +704,16 @@ static void clear_diffin(diffin_T *din)
if (din->din_fname == NULL) {
XFREE_CLEAR(din->din_mmfile.ptr);
} else {
- os_remove((char *)din->din_fname);
+ os_remove(din->din_fname);
}
}
static void clear_diffout(diffout_T *dout)
{
if (dout->dout_fname == NULL) {
- ga_clear_strings(&dout->dout_ga);
+ ga_clear(&dout->dout_ga);
} else {
- os_remove((char *)dout->dout_fname);
+ os_remove(dout->dout_fname);
}
}
@@ -731,7 +731,7 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
len += (long)strlen(ml_get_buf(buf, lnum, false)) + 1;
}
- char_u *ptr = try_malloc((size_t)len);
+ char *ptr = try_malloc((size_t)len);
if (ptr == NULL) {
// Allocating memory failed. This can happen, because we try to read
// the whole buffer text into memory. Set the failed flag, the diff
@@ -745,12 +745,12 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
}
return FAIL;
}
- din->din_mmfile.ptr = (char *)ptr;
+ din->din_mmfile.ptr = ptr;
din->din_mmfile.size = len;
len = 0;
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
- for (char_u *s = (char_u *)ml_get_buf(buf, lnum, false); *s != NUL;) {
+ for (char *s = ml_get_buf(buf, lnum, false); *s != NUL;) {
if (diff_flags & DIFF_ICASE) {
int c;
char cbuf[MB_MAXBYTES + 1];
@@ -759,11 +759,11 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
c = NUL;
} else {
// xdiff doesn't support ignoring case, fold-case the text.
- c = utf_ptr2char((char *)s);
+ c = utf_ptr2char(s);
c = utf_fold(c);
}
- const int orig_len = utfc_ptr2len((char *)s);
- if (utf_char2bytes(c, (char *)cbuf) != orig_len) {
+ const int orig_len = utfc_ptr2len(s);
+ if (utf_char2bytes(c, cbuf) != orig_len) {
// TODO(Bram): handle byte length difference
memmove(ptr + len, s, (size_t)orig_len);
} else {
@@ -803,7 +803,7 @@ static int diff_write(buf_T *buf, diffin_T *din)
// Writing the buffer is an implementation detail of performing the diff,
// so it shouldn't update the '[ and '] marks.
cmdmod.cmod_flags |= CMOD_LOCKMARKS;
- int r = buf_write(buf, (char *)din->din_fname, NULL,
+ int r = buf_write(buf, din->din_fname, NULL,
(linenr_T)1, buf->b_ml.ml_line_count,
NULL, false, false, false, true);
cmdmod.cmod_flags = save_cmod_flags;
@@ -821,12 +821,12 @@ static int diff_write(buf_T *buf, diffin_T *din)
static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap)
{
if (dio->dio_internal) {
- ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000);
+ ga_init(&dio->dio_diff.dout_ga, sizeof(diffhunk_T), 100);
} else {
// We need three temp file names.
- dio->dio_orig.din_fname = (char_u *)vim_tempname();
- dio->dio_new.din_fname = (char_u *)vim_tempname();
- dio->dio_diff.dout_fname = (char_u *)vim_tempname();
+ dio->dio_orig.din_fname = vim_tempname();
+ dio->dio_new.din_fname = vim_tempname();
+ dio->dio_diff.dout_fname = vim_tempname();
if (dio->dio_orig.din_fname == NULL
|| dio->dio_new.din_fname == NULL
|| dio->dio_diff.dout_fname == NULL) {
@@ -992,7 +992,7 @@ static int check_external_diff(diffio_T *diffio)
TriState ok = kFalse;
for (;;) {
ok = kFalse;
- FILE *fd = os_fopen((char *)diffio->dio_orig.din_fname, "w");
+ FILE *fd = os_fopen(diffio->dio_orig.din_fname, "w");
if (fd == NULL) {
io_error = true;
@@ -1001,7 +1001,7 @@ static int check_external_diff(diffio_T *diffio)
io_error = true;
}
fclose(fd);
- fd = os_fopen((char *)diffio->dio_new.din_fname, "w");
+ fd = os_fopen(diffio->dio_new.din_fname, "w");
if (fd == NULL) {
io_error = true;
@@ -1012,18 +1012,18 @@ static int check_external_diff(diffio_T *diffio)
fclose(fd);
fd = NULL;
if (diff_file(diffio) == OK) {
- fd = os_fopen((char *)diffio->dio_diff.dout_fname, "r");
+ fd = os_fopen(diffio->dio_diff.dout_fname, "r");
}
if (fd == NULL) {
io_error = true;
} else {
- char_u linebuf[LBUFLEN];
+ char linebuf[LBUFLEN];
for (;;) {
// For normal diff there must be a line that contains
// "1c1". For unified diff "@@ -1 +1 @@".
- if (vim_fgets(linebuf, LBUFLEN, fd)) {
+ if (vim_fgets((char_u *)linebuf, LBUFLEN, fd)) {
break;
}
@@ -1034,10 +1034,10 @@ static int check_external_diff(diffio_T *diffio)
}
fclose(fd);
}
- os_remove((char *)diffio->dio_diff.dout_fname);
- os_remove((char *)diffio->dio_new.din_fname);
+ os_remove(diffio->dio_diff.dout_fname);
+ os_remove(diffio->dio_new.din_fname);
}
- os_remove((char *)diffio->dio_orig.din_fname);
+ os_remove(diffio->dio_orig.din_fname);
}
// When using 'diffexpr' break here.
@@ -1115,9 +1115,9 @@ static int diff_file_internal(diffio_T *diffio)
/// @return OK or FAIL
static int diff_file(diffio_T *dio)
{
- char *tmp_orig = (char *)dio->dio_orig.din_fname;
- char *tmp_new = (char *)dio->dio_new.din_fname;
- char *tmp_diff = (char *)dio->dio_diff.dout_fname;
+ char *tmp_orig = dio->dio_orig.din_fname;
+ char *tmp_new = dio->dio_new.din_fname;
+ char *tmp_diff = dio->dio_diff.dout_fname;
if (*p_dex != NUL) {
// Use 'diffexpr' to generate the diff file.
eval_diff(tmp_orig, tmp_new, tmp_diff);
@@ -1167,10 +1167,10 @@ static int diff_file(diffio_T *dio)
/// @param eap
void ex_diffpatch(exarg_T *eap)
{
- char_u *buf = NULL;
+ char *buf = NULL;
win_T *old_curwin = curwin;
char *newname = NULL; // name of patched file buffer
- char_u *esc_name = NULL;
+ char *esc_name = NULL;
#ifdef UNIX
char *fullname = NULL;
@@ -1178,16 +1178,16 @@ void ex_diffpatch(exarg_T *eap)
// We need two temp file names.
// Name of original temp file.
- char_u *tmp_orig = (char_u *)vim_tempname();
+ char *tmp_orig = vim_tempname();
// Name of patched temp file.
- char_u *tmp_new = (char_u *)vim_tempname();
+ char *tmp_new = vim_tempname();
if ((tmp_orig == NULL) || (tmp_new == NULL)) {
goto theend;
}
// Write the current buffer to "tmp_orig".
- if (buf_write(curbuf, (char *)tmp_orig, NULL,
+ if (buf_write(curbuf, tmp_orig, NULL,
(linenr_T)1, curbuf->b_ml.ml_line_count,
NULL, false, false, false, true) == FAIL) {
goto theend;
@@ -1197,7 +1197,7 @@ void ex_diffpatch(exarg_T *eap)
// Get the absolute path of the patchfile, changing directory below.
fullname = FullName_save(eap->arg, false);
esc_name =
- vim_strsave_shellescape((char_u *)(fullname != NULL ? fullname : eap->arg), true, true);
+ (char *)vim_strsave_shellescape((char_u *)(fullname != NULL ? fullname : eap->arg), true, true);
#else
esc_name = vim_strsave_shellescape(eap->arg, true, true);
#endif
@@ -1205,14 +1205,14 @@ void ex_diffpatch(exarg_T *eap)
buf = xmalloc(buflen);
#ifdef UNIX
- char_u dirbuf[MAXPATHL];
+ char dirbuf[MAXPATHL];
// Temporarily chdir to /tmp, to avoid patching files in the current
// directory when the patch file contains more than one patch. When we
// have our own temp dir use that instead, it will be cleaned up when we
// exit (any .rej files created). Don't change directory if we can't
// return to the current.
- if ((os_dirname(dirbuf, MAXPATHL) != OK)
- || (os_chdir((char *)dirbuf) != 0)) {
+ if ((os_dirname((char_u *)dirbuf, MAXPATHL) != OK)
+ || (os_chdir(dirbuf) != 0)) {
dirbuf[0] = NUL;
} else {
char *tempdir = vim_gettempdir();
@@ -1227,24 +1227,22 @@ void ex_diffpatch(exarg_T *eap)
if (*p_pex != NUL) {
// Use 'patchexpr' to generate the new file.
#ifdef UNIX
- eval_patch((char *)tmp_orig,
- (fullname != NULL ? fullname : eap->arg),
- (char *)tmp_new);
+ eval_patch(tmp_orig, (fullname != NULL ? fullname : eap->arg), tmp_new);
#else
- eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new);
+ eval_patch(tmp_orig, eap->arg, tmp_new);
#endif
} else {
// Build the patch command and execute it. Ignore errors.
- vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s",
+ vim_snprintf(buf, buflen, "patch -o %s %s < %s",
tmp_new, tmp_orig, esc_name);
block_autocmds(); // Avoid ShellCmdPost stuff
- (void)call_shell(buf, kShellOptFilter, NULL);
+ (void)call_shell((char_u *)buf, kShellOptFilter, NULL);
unblock_autocmds();
}
#ifdef UNIX
if (dirbuf[0] != NUL) {
- if (os_chdir((char *)dirbuf) != 0) {
+ if (os_chdir(dirbuf) != 0) {
emsg(_(e_prev_dir));
}
shorten_fnames(true);
@@ -1254,14 +1252,14 @@ void ex_diffpatch(exarg_T *eap)
// Delete any .orig or .rej file created.
STRCPY(buf, tmp_new);
STRCAT(buf, ".orig");
- os_remove((char *)buf);
+ os_remove(buf);
STRCPY(buf, tmp_new);
STRCAT(buf, ".rej");
- os_remove((char *)buf);
+ os_remove(buf);
// Only continue if the output file was created.
FileInfo file_info;
- bool info_ok = os_fileinfo((char *)tmp_new, &file_info);
+ bool info_ok = os_fileinfo(tmp_new, &file_info);
uint64_t filesize = os_fileinfo_size(&file_info);
if (!info_ok || filesize == 0) {
emsg(_("E816: Cannot read patch output"));
@@ -1277,7 +1275,7 @@ void ex_diffpatch(exarg_T *eap)
if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) {
// Pretend it was a ":split fname" command
eap->cmdidx = CMD_split;
- eap->arg = (char *)tmp_new;
+ eap->arg = tmp_new;
do_exedit(eap, old_curwin);
// check that split worked and editing tmp_new
@@ -1302,12 +1300,12 @@ void ex_diffpatch(exarg_T *eap)
theend:
if (tmp_orig != NULL) {
- os_remove((char *)tmp_orig);
+ os_remove(tmp_orig);
}
xfree(tmp_orig);
if (tmp_new != NULL) {
- os_remove((char *)tmp_new);
+ os_remove(tmp_new);
}
xfree(tmp_new);
xfree(newname);
@@ -1526,210 +1524,219 @@ void ex_diffoff(exarg_T *eap)
}
}
-/// Read the diff output and add each entry to the diff list.
-///
-/// @param idx_orig idx of original file
-/// @param idx_new idx of new file
-/// @dout diff output
-static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
+static bool extract_hunk_internal(diffout_T *dout, diffhunk_T *hunk, int *line_idx)
{
- FILE *fd = NULL;
- int line_idx = 0;
- diff_T *dprev = NULL;
- diff_T *dp = curtab->tp_first_diff;
- diff_T *dn, *dpl;
- diffout_T *dout = &dio->dio_diff;
- char linebuf[LBUFLEN]; // only need to hold the diff line
- char *line;
- linenr_T off;
- int i;
- int notset = true; // block "*dp" not set yet
- diffhunk_T *hunk = NULL; // init to avoid gcc warning
- enum {
- DIFF_ED,
- DIFF_UNIFIED,
- DIFF_NONE,
- } diffstyle = DIFF_NONE;
-
- if (dout->dout_fname == NULL) {
- diffstyle = DIFF_UNIFIED;
- } else {
- fd = os_fopen((char *)dout->dout_fname, "r");
- if (fd == NULL) {
- emsg(_("E98: Cannot read diff output"));
- return;
- }
- }
-
- if (!dio->dio_internal) {
- hunk = xmalloc(sizeof(*hunk));
+ bool eof = *line_idx >= dout->dout_ga.ga_len;
+ if (!eof) {
+ *hunk = ((diffhunk_T *)dout->dout_ga.ga_data)[(*line_idx)++];
}
+ return eof;
+}
+// Extract hunk by parsing the diff output from file and calculate the diffstyle.
+static bool extract_hunk(FILE *fd, diffhunk_T *hunk, diffstyle_T *diffstyle)
+{
for (;;) {
- if (dio->dio_internal) {
- if (line_idx >= dout->dout_ga.ga_len) {
- break; // did last line
- }
- hunk = ((diffhunk_T **)dout->dout_ga.ga_data)[line_idx++];
- } else {
- if (fd == NULL) {
- if (line_idx >= dout->dout_ga.ga_len) {
- break; // did last line
- }
- line = ((char **)dout->dout_ga.ga_data)[line_idx++];
+ char line[LBUFLEN]; // only need to hold the diff line
+ if (vim_fgets((char_u *)line, LBUFLEN, fd)) {
+ return true; // end of file
+ }
+
+ if (*diffstyle == DIFF_NONE) {
+ // Determine diff style.
+ // ed like diff looks like this:
+ // {first}[,{last}]c{first}[,{last}]
+ // {first}a{first}[,{last}]
+ // {first}[,{last}]d{first}
+ //
+ // unified diff looks like this:
+ // --- file1 2018-03-20 13:23:35.783153140 +0100
+ // +++ file2 2018-03-20 13:23:41.183156066 +0100
+ // @@ -1,3 +1,5 @@
+ if (isdigit(*line)) {
+ *diffstyle = DIFF_ED;
+ } else if ((STRNCMP(line, "@@ ", 3) == 0)) {
+ *diffstyle = DIFF_UNIFIED;
+ } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501
+ && (vim_fgets((char_u *)line, LBUFLEN, fd) == 0) // -V501
+ && (STRNCMP(line, "+++ ", 4) == 0)
+ && (vim_fgets((char_u *)line, LBUFLEN, fd) == 0) // -V501
+ && (STRNCMP(line, "@@ ", 3) == 0)) {
+ *diffstyle = DIFF_UNIFIED;
} else {
- if (vim_fgets((char_u *)linebuf, LBUFLEN, fd)) {
- break; // end of file
- }
- line = linebuf;
+ // Format not recognized yet, skip over this line. Cygwin diff
+ // may put a warning at the start of the file.
+ continue;
}
+ }
- if (diffstyle == DIFF_NONE) {
- // Determine diff style.
- // ed like diff looks like this:
- // {first}[,{last}]c{first}[,{last}]
- // {first}a{first}[,{last}]
- // {first}[,{last}]d{first}
- //
- // unified diff looks like this:
- // --- file1 2018-03-20 13:23:35.783153140 +0100
- // +++ file2 2018-03-20 13:23:41.183156066 +0100
- // @@ -1,3 +1,5 @@
- if (isdigit(*line)) {
- diffstyle = DIFF_ED;
- } else if ((STRNCMP(line, "@@ ", 3) == 0)) {
- diffstyle = DIFF_UNIFIED;
- } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501
- && (vim_fgets((char_u *)linebuf, LBUFLEN, fd) == 0) // -V501
- && (STRNCMP(line, "+++ ", 4) == 0)
- && (vim_fgets((char_u *)linebuf, LBUFLEN, fd) == 0) // -V501
- && (STRNCMP(line, "@@ ", 3) == 0)) {
- diffstyle = DIFF_UNIFIED;
- } else {
- // Format not recognized yet, skip over this line. Cygwin diff
- // may put a warning at the start of the file.
- continue;
- }
+ if (*diffstyle == DIFF_ED) {
+ if (!isdigit(*line)) {
+ continue; // not the start of a diff block
}
-
- if (diffstyle == DIFF_ED) {
- if (!isdigit(*line)) {
- continue; // not the start of a diff block
- }
- if (parse_diff_ed((char_u *)line, hunk) == FAIL) {
- continue;
- }
- } else {
- assert(diffstyle == DIFF_UNIFIED);
- if (STRNCMP(line, "@@ ", 3) != 0) {
- continue; // not the start of a diff block
- }
- if (parse_diff_unified((char_u *)line, hunk) == FAIL) {
- continue;
- }
+ if (parse_diff_ed(line, hunk) == FAIL) {
+ continue;
}
- }
-
- // Go over blocks before the change, for which orig and new are equal.
- // Copy blocks from orig to new.
- while (dp != NULL
- && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) {
- if (notset) {
- diff_copy_entry(dprev, dp, idx_orig, idx_new);
+ } else {
+ assert(*diffstyle == DIFF_UNIFIED);
+ if (STRNCMP(line, "@@ ", 3) != 0) {
+ continue; // not the start of a diff block
+ }
+ if (parse_diff_unified(line, hunk) == FAIL) {
+ continue;
}
- dprev = dp;
- dp = dp->df_next;
- notset = true;
}
- if ((dp != NULL)
- && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
- && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) {
- // New block overlaps with existing block(s).
- // First find last block that overlaps.
- for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) {
- if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) {
- break;
- }
- }
+ // Successfully parsed diff output, can return
+ return false;
+ }
+}
- // If the newly found block starts before the old one, set the
- // start back a number of lines.
- off = dp->df_lnum[idx_orig] - hunk->lnum_orig;
+static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_new, diffhunk_T *hunk,
+ bool *notsetp)
+{
+ diff_T *dp = *dpp;
+ diff_T *dprev = *dprevp;
+
+ // Go over blocks before the change, for which orig and new are equal.
+ // Copy blocks from orig to new.
+ while (dp != NULL
+ && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) {
+ if (*notsetp) {
+ diff_copy_entry(dprev, dp, idx_orig, idx_new);
+ }
+ dprev = dp;
+ dp = dp->df_next;
+ *notsetp = true;
+ }
- if (off > 0) {
- for (i = idx_orig; i < idx_new; i++) {
- if (curtab->tp_diffbuf[i] != NULL) {
- dp->df_lnum[i] -= off;
- }
- }
- dp->df_lnum[idx_new] = hunk->lnum_new;
- dp->df_count[idx_new] = (linenr_T)hunk->count_new;
- } else if (notset) {
- // new block inside existing one, adjust new block
- dp->df_lnum[idx_new] = hunk->lnum_new + off;
- dp->df_count[idx_new] = (linenr_T)hunk->count_new - off;
- } else {
- // second overlap of new block with existing block
- dp->df_count[idx_new] += (linenr_T)hunk->count_new - (linenr_T)hunk->count_orig
- + dpl->df_lnum[idx_orig] +
- dpl->df_count[idx_orig]
- - (dp->df_lnum[idx_orig] +
- dp->df_count[idx_orig]);
+ if ((dp != NULL)
+ && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
+ && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) {
+ // New block overlaps with existing block(s).
+ // First find last block that overlaps.
+ diff_T *dpl;
+ for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) {
+ if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) {
+ break;
}
+ }
- // Adjust the size of the block to include all the lines to the
- // end of the existing block or the new diff, whatever ends last.
- off = (hunk->lnum_orig + (linenr_T)hunk->count_orig)
- - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
+ // If the newly found block starts before the old one, set the
+ // start back a number of lines.
+ linenr_T off = dp->df_lnum[idx_orig] - hunk->lnum_orig;
- if (off < 0) {
- // new change ends in existing block, adjust the end if not
- // done already
- if (notset) {
- dp->df_count[idx_new] += -off;
+ if (off > 0) {
+ for (int i = idx_orig; i < idx_new; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ dp->df_lnum[i] -= off;
}
- off = 0;
}
+ dp->df_lnum[idx_new] = hunk->lnum_new;
+ dp->df_count[idx_new] = (linenr_T)hunk->count_new;
+ } else if (*notsetp) {
+ // new block inside existing one, adjust new block
+ dp->df_lnum[idx_new] = hunk->lnum_new + off;
+ dp->df_count[idx_new] = (linenr_T)hunk->count_new - off;
+ } else {
+ // second overlap of new block with existing block
+ dp->df_count[idx_new] += (linenr_T)hunk->count_new - (linenr_T)hunk->count_orig
+ + dpl->df_lnum[idx_orig] +
+ dpl->df_count[idx_orig]
+ - (dp->df_lnum[idx_orig] +
+ dp->df_count[idx_orig]);
+ }
+
+ // Adjust the size of the block to include all the lines to the
+ // end of the existing block or the new diff, whatever ends last.
+ off = (hunk->lnum_orig + (linenr_T)hunk->count_orig)
+ - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
+
+ if (off < 0) {
+ // new change ends in existing block, adjust the end if not
+ // done already
+ if (*notsetp) {
+ dp->df_count[idx_new] += -off;
+ }
+ off = 0;
+ }
- for (i = idx_orig; i < idx_new; i++) {
- if (curtab->tp_diffbuf[i] != NULL) {
- dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i]
- - dp->df_lnum[i] + off;
- }
+ for (int i = idx_orig; i < idx_new; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i]
+ - dp->df_lnum[i] + off;
}
+ }
- // Delete the diff blocks that have been merged into one.
- dn = dp->df_next;
- dp->df_next = dpl->df_next;
+ // Delete the diff blocks that have been merged into one.
+ diff_T *dn = dp->df_next;
+ dp->df_next = dpl->df_next;
- while (dn != dp->df_next) {
- dpl = dn->df_next;
- xfree(dn);
- dn = dpl;
+ while (dn != dp->df_next) {
+ dpl = dn->df_next;
+ xfree(dn);
+ dn = dpl;
+ }
+ } else {
+ // Allocate a new diffblock.
+ dp = diff_alloc_new(curtab, dprev, dp);
+
+ dp->df_lnum[idx_orig] = hunk->lnum_orig;
+ dp->df_count[idx_orig] = (linenr_T)hunk->count_orig;
+ dp->df_lnum[idx_new] = hunk->lnum_new;
+ dp->df_count[idx_new] = (linenr_T)hunk->count_new;
+
+ // Set values for other buffers, these must be equal to the
+ // original buffer, otherwise there would have been a change
+ // already.
+ for (int i = idx_orig + 1; i < idx_new; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ diff_copy_entry(dprev, dp, idx_orig, i);
}
- } else {
- // Allocate a new diffblock.
- dp = diff_alloc_new(curtab, dprev, dp);
+ }
+ }
+ *notsetp = false; // "*dp" has been set
+ *dpp = dp;
+ *dprevp = dprev;
+}
- dp->df_lnum[idx_orig] = hunk->lnum_orig;
- dp->df_count[idx_orig] = (linenr_T)hunk->count_orig;
- dp->df_lnum[idx_new] = hunk->lnum_new;
- dp->df_count[idx_new] = (linenr_T)hunk->count_new;
+/// Read the diff output and add each entry to the diff list.
+///
+/// @param idx_orig idx of original file
+/// @param idx_new idx of new file
+/// @dout diff output
+static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
+{
+ FILE *fd = NULL;
+ int line_idx = 0;
+ diff_T *dprev = NULL;
+ diff_T *dp = curtab->tp_first_diff;
+ diffout_T *dout = &dio->dio_diff;
+ bool notset = true; // block "*dp" not set yet
+ diffstyle_T diffstyle = DIFF_NONE;
- // Set values for other buffers, these must be equal to the
- // original buffer, otherwise there would have been a change
- // already.
- for (i = idx_orig + 1; i < idx_new; i++) {
- if (curtab->tp_diffbuf[i] != NULL) {
- diff_copy_entry(dprev, dp, idx_orig, i);
- }
- }
+ if (!dio->dio_internal) {
+ fd = os_fopen(dout->dout_fname, "r");
+ if (fd == NULL) {
+ emsg(_("E98: Cannot read diff output"));
+ return;
+ }
+ }
+
+ for (;;) {
+ diffhunk_T hunk = { 0 };
+ bool eof = dio->dio_internal
+ ? extract_hunk_internal(dout, &hunk, &line_idx)
+ : extract_hunk(fd, &hunk, &diffstyle);
+
+ if (eof) {
+ break;
}
- notset = false; // "*dp" has been set
+
+ process_hunk(&dp, &dprev, idx_orig, idx_new, &hunk, &notset);
}
-// for remaining diff blocks orig and new are equal
+ // for remaining diff blocks orig and new are equal
while (dp != NULL) {
if (notset) {
diff_copy_entry(dprev, dp, idx_orig, idx_new);
@@ -1739,10 +1746,6 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
notset = true;
}
- if (!dio->dio_internal) {
- xfree(hunk);
- }
-
if (fd != NULL) {
fclose(fd);
}
@@ -1797,7 +1800,6 @@ void diff_clear(tabpage_T *tp)
/// @return diff status.
int diff_check(win_T *wp, linenr_T lnum)
{
- diff_T *dp;
buf_T *buf = wp->w_buffer;
if (curtab->tp_diff_invalid) {
@@ -1828,6 +1830,7 @@ int diff_check(win_T *wp, linenr_T lnum)
}
// search for a change that includes "lnum" in the list of diffblocks.
+ diff_T *dp;
for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
break;
@@ -1936,24 +1939,24 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
// Compare the characters at "p1" and "p2". If they are equal (possibly
// ignoring case) return true and set "len" to the number of bytes.
-static bool diff_equal_char(const char_u *const p1, const char_u *const p2, int *const len)
+static bool diff_equal_char(const char *const p1, const char *const p2, int *const len)
{
- const int l = utfc_ptr2len((char *)p1);
+ const int l = utfc_ptr2len(p1);
- if (l != utfc_ptr2len((char *)p2)) {
+ if (l != utfc_ptr2len(p2)) {
return false;
}
if (l > 1) {
if (STRNCMP(p1, p2, l) != 0
&& (!(diff_flags & DIFF_ICASE)
- || utf_fold(utf_ptr2char((char *)p1)) != utf_fold(utf_ptr2char((char *)p2)))) {
+ || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) {
return false;
}
*len = l;
} else {
if ((*p1 != *p2)
&& (!(diff_flags & DIFF_ICASE)
- || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) {
+ || TOLOWER_LOC((uint8_t)(*p1)) != TOLOWER_LOC((uint8_t)(*p2)))) {
return false;
}
*len = 1;
@@ -1980,7 +1983,7 @@ static int diff_cmp(char *s1, char *s2)
}
if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) {
- return mb_stricmp((const char *)s1, (const char *)s2);
+ return mb_stricmp(s1, s2);
}
char *p1 = s1;
@@ -1996,7 +1999,7 @@ static int diff_cmp(char *s1, char *s2)
p2 = skipwhite(p2);
} else {
int l;
- if (!diff_equal_char((char_u *)p1, (char_u *)p2, &l)) {
+ if (!diff_equal_char(p1, p2, &l)) {
break;
}
p1 += l;
@@ -2023,7 +2026,6 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
{
buf_T *frombuf = fromwin->w_buffer;
linenr_T lnum = fromwin->w_topline;
- diff_T *dp;
int fromidx = diff_buf_idx(frombuf);
if (fromidx == DB_COUNT) {
@@ -2038,6 +2040,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
towin->w_topfill = 0;
// search for a change that includes "lnum" in the list of diffblocks.
+ diff_T *dp;
for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
break;
@@ -2281,14 +2284,6 @@ bool diffopt_filler(void)
bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- char *line_new;
- int si_org;
- int si_new;
- int ei_org;
- int ei_new;
- bool added = true;
- int l;
-
// Make a copy of the line, the next ml_get() will invalidate it.
char *line_org = xstrdup(ml_get_buf(wp->w_buffer, lnum, false));
@@ -2313,6 +2308,12 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
return false;
}
+ int si_org;
+ int si_new;
+ int ei_org;
+ int ei_new;
+ bool added = true;
+
linenr_T off = lnum - dp->df_lnum[idx];
int i;
for (i = 0; i < DB_COUNT; i++) {
@@ -2322,7 +2323,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
continue;
}
added = false;
- line_new = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off, false);
+ char *line_new = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off, false);
// Search for start of difference
si_org = si_new = 0;
@@ -2337,7 +2338,8 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
si_org = (int)(skipwhite(line_org + si_org) - line_org);
si_new = (int)(skipwhite(line_new + si_new) - line_new);
} else {
- if (!diff_equal_char((char_u *)line_org + si_org, (char_u *)line_new + si_new, &l)) {
+ int l;
+ if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) {
break;
}
si_org += l;
@@ -2377,12 +2379,13 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
ei_new--;
}
} else {
- const char_u *p1 = (char_u *)line_org + ei_org;
- const char_u *p2 = (char_u *)line_new + ei_new;
+ const char *p1 = line_org + ei_org;
+ const char *p2 = line_new + ei_new;
- p1 -= utf_head_off(line_org, (char *)p1);
- p2 -= utf_head_off(line_new, (char *)p2);
+ p1 -= utf_head_off(line_org, p1);
+ p2 -= utf_head_off(line_new, p2);
+ int l;
if (!diff_equal_char(p1, p2, &l)) {
break;
}
@@ -2412,16 +2415,14 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
bool diff_infold(win_T *wp, linenr_T lnum)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
{
- bool other = false;
-
// Return if 'diff' isn't set.
if (!wp->w_p_diff) {
return false;
}
int idx = -1;
- int i;
- for (i = 0; i < DB_COUNT; i++) {
+ bool other = false;
+ for (int i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] == wp->w_buffer) {
idx = i;
} else if (curtab->tp_diffbuf[i] != NULL) {
@@ -2461,13 +2462,13 @@ bool diff_infold(win_T *wp, linenr_T lnum)
/// "dp" and "do" commands.
void nv_diffgetput(bool put, size_t count)
{
- exarg_T ea;
- char buf[30];
-
if (bt_prompt(curbuf)) {
vim_beep(BO_OPER);
return;
}
+
+ exarg_T ea;
+ char buf[30];
if (count == 0) {
ea.arg = "";
} else {
@@ -2503,24 +2504,7 @@ static bool valid_diff(diff_T *diff)
/// @param eap
void ex_diffgetput(exarg_T *eap)
{
- linenr_T lnum;
- linenr_T count;
- linenr_T off = 0;
- diff_T *dp;
- diff_T *dfree;
- int i;
- int added;
- char *p;
- aco_save_T aco;
- buf_T *buf;
- linenr_T start_skip;
- linenr_T end_skip;
- linenr_T new_count;
- int buf_empty;
- int found_not_ma = false;
int idx_other;
- int idx_from;
- int idx_to;
// Find the current buffer in the list of diff buffers.
int idx_cur = diff_buf_idx(curbuf);
@@ -2530,6 +2514,7 @@ void ex_diffgetput(exarg_T *eap)
}
if (*eap->arg == NUL) {
+ int found_not_ma = false;
// No argument: Find the other buffer in the list of diff buffers.
for (idx_other = 0; idx_other < DB_COUNT; idx_other++) {
if ((curtab->tp_diffbuf[idx_other] != curbuf)
@@ -2552,7 +2537,7 @@ void ex_diffgetput(exarg_T *eap)
}
// Check that there isn't a third buffer in the list
- for (i = idx_other + 1; i < DB_COUNT; i++) {
+ for (int i = idx_other + 1; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != curbuf)
&& (curtab->tp_diffbuf[i] != NULL)
&& ((eap->cmdidx != CMD_diffput)
@@ -2564,11 +2549,12 @@ void ex_diffgetput(exarg_T *eap)
}
} else {
// Buffer number or pattern given. Ignore trailing white space.
- p = eap->arg + strlen(eap->arg);
+ char *p = eap->arg + strlen(eap->arg);
while (p > eap->arg && ascii_iswhite(p[-1])) {
p--;
}
+ int i;
for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; i++) {}
if (eap->arg + i == p) {
@@ -2582,7 +2568,7 @@ void ex_diffgetput(exarg_T *eap)
return;
}
}
- buf = buflist_findnr(i);
+ buf_T *buf = buflist_findnr(i);
if (buf == NULL) {
semsg(_("E102: Can't find buffer \"%s\""), eap->arg);
@@ -2617,13 +2603,9 @@ void ex_diffgetput(exarg_T *eap)
}
}
- if (eap->cmdidx == CMD_diffget) {
- idx_from = idx_other;
- idx_to = idx_cur;
- } else {
- idx_from = idx_cur;
- idx_to = idx_other;
+ aco_save_T aco;
+ if (eap->cmdidx != CMD_diffget) {
// Need to make the other buffer the current buffer to be able to make
// changes in it.
@@ -2631,6 +2613,9 @@ void ex_diffgetput(exarg_T *eap)
aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]);
}
+ const int idx_from = eap->cmdidx == CMD_diffget ? idx_other : idx_cur;
+ const int idx_to = eap->cmdidx == CMD_diffget ? idx_cur : idx_other;
+
// May give the warning for a changed buffer here, which can trigger the
// FileChangedRO autocommand, which may do nasty things and mess
// everything up.
@@ -2642,26 +2627,71 @@ void ex_diffgetput(exarg_T *eap)
}
}
+ diffgetput(eap->addr_count, idx_cur, idx_from, idx_to, eap->line1, eap->line2);
+
+ // restore curwin/curbuf and a few other things
+ if (eap->cmdidx != CMD_diffget) {
+ // Syncing undo only works for the current buffer, but we change
+ // another buffer. Sync undo if the command was typed. This isn't
+ // 100% right when ":diffput" is used in a function or mapping.
+ if (KeyTyped) {
+ u_sync(false);
+ }
+ aucmd_restbuf(&aco);
+ }
+
+theend:
+ diff_busy = false;
+
+ if (diff_need_update) {
+ ex_diffupdate(NULL);
+ }
+
+ // Check that the cursor is on a valid character and update its
+ // position. When there were filler lines the topline has become
+ // invalid.
+ check_cursor();
+ changed_line_abv_curs();
+
+ if (diff_need_update) {
+ // redraw already done by ex_diffupdate()
+ diff_need_update = false;
+ } else {
+ // Also need to redraw the other buffers.
+ diff_redraw(false);
+ apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf);
+ }
+}
+
+/// Apply diffget/diffput to buffers and diffblocks
+///
+/// @param idx_cur index of "curbuf" before aucmd_prepbuf() in the list of diff buffers
+/// @param idx_from index of the buffer to read from in the list of diff buffers
+/// @param idx_to index of the buffer to modify in the list of diff buffers
+static void diffgetput(const int addr_count, const int idx_cur, const int idx_from,
+ const int idx_to, const linenr_T line1, const linenr_T line2)
+{
+ linenr_T off = 0;
diff_T *dprev = NULL;
- for (dp = curtab->tp_first_diff; dp != NULL;) {
- if (dp->df_lnum[idx_cur] > eap->line2 + off) {
+ for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) {
+ if (dp->df_lnum[idx_cur] > line2 + off) {
// past the range that was specified
break;
}
- dfree = NULL;
- lnum = dp->df_lnum[idx_to];
- count = dp->df_count[idx_to];
+ diff_T *dfree = NULL;
+ linenr_T lnum = dp->df_lnum[idx_to];
+ linenr_T count = dp->df_count[idx_to];
- if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off)
+ if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > line1 + off)
&& (u_save(lnum - 1, lnum + count) != FAIL)) {
// Inside the specified range and saving for undo worked.
- start_skip = 0;
- end_skip = 0;
+ linenr_T start_skip = 0;
+ linenr_T end_skip = 0;
- if (eap->addr_count > 0) {
+ if (addr_count > 0) {
// A range was specified: check if lines need to be skipped.
- start_skip = eap->line1 + off - dp->df_lnum[idx_cur];
+ start_skip = line1 + off - dp->df_lnum[idx_cur];
if (start_skip > 0) {
// range starts below start of current diff block
if (start_skip > count) {
@@ -2676,13 +2706,13 @@ void ex_diffgetput(exarg_T *eap)
}
end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1
- - (eap->line2 + off);
+ - (line2 + off);
if (end_skip > 0) {
// range ends above end of current/from diff block
if (idx_cur == idx_from) {
// :diffput
- i = dp->df_count[idx_cur] - start_skip - end_skip;
+ int i = dp->df_count[idx_cur] - start_skip - end_skip;
if (count > i) {
count = i;
@@ -2701,10 +2731,10 @@ void ex_diffgetput(exarg_T *eap)
}
}
- buf_empty = buf_is_empty(curbuf);
- added = 0;
+ bool buf_empty = buf_is_empty(curbuf);
+ int added = 0;
- for (i = 0; i < count; i++) {
+ for (int i = 0; i < count; i++) {
// remember deleting the last line of the buffer
buf_empty = curbuf->b_ml.ml_line_count == 1;
if (ml_delete(lnum, false) == OK) {
@@ -2712,12 +2742,12 @@ void ex_diffgetput(exarg_T *eap)
}
}
- for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) {
+ for (int i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) {
linenr_T nr = dp->df_lnum[idx_from] + start_skip + i;
if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) {
break;
}
- p = xstrdup(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false));
+ char *p = xstrdup(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false));
ml_append(lnum + i - 1, p, 0, false);
xfree(p);
added++;
@@ -2728,12 +2758,13 @@ void ex_diffgetput(exarg_T *eap)
ml_delete((linenr_T)2, false);
}
}
- new_count = dp->df_count[idx_to] + added;
+ linenr_T new_count = dp->df_count[idx_to] + added;
dp->df_count[idx_to] = new_count;
if ((start_skip == 0) && (end_skip == 0)) {
// Check if there are any other buffers and if the diff is
// equal in them.
+ int i;
for (i = 0; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != NULL)
&& (i != idx_from)
@@ -2800,38 +2831,6 @@ void ex_diffgetput(exarg_T *eap)
dp = dp->df_next;
}
}
-
- // restore curwin/curbuf and a few other things
- if (eap->cmdidx != CMD_diffget) {
- // Syncing undo only works for the current buffer, but we change
- // another buffer. Sync undo if the command was typed. This isn't
- // 100% right when ":diffput" is used in a function or mapping.
- if (KeyTyped) {
- u_sync(false);
- }
- aucmd_restbuf(&aco);
- }
-
-theend:
- diff_busy = false;
- if (diff_need_update) {
- ex_diffupdate(NULL);
- }
-
- // Check that the cursor is on a valid character and update its
- // position. When there were filler lines the topline has become
- // invalid.
- check_cursor();
- changed_line_abv_curs();
-
- if (diff_need_update) {
- // redraw already done by ex_diffupdate()
- diff_need_update = false;
- } else {
- // Also need to redraw the other buffers.
- diff_redraw(false);
- apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf);
- }
}
/// Update folds for all diff buffers for entry "dp".
@@ -3055,7 +3054,7 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp)
/// Handle an ED style diff line.
/// Return FAIL if the line does not contain diff info.
///
-static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
+static int parse_diff_ed(char *line, diffhunk_T *hunk)
{
long l1, l2;
@@ -3063,7 +3062,7 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
// change: {first}[,{last}]c{first}[,{last}]
// append: {first}a{first}[,{last}]
// delete: {first}[,{last}]d{first}
- char *p = (char *)line;
+ char *p = line;
linenr_T f1 = getdigits_int32(&p, true, 0);
if (*p == ',') {
p++;
@@ -3107,11 +3106,11 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
/// Parses unified diff with zero(!) context lines.
/// Return FAIL if there is no diff information in "line".
///
-static int parse_diff_unified(char_u *line, diffhunk_T *hunk)
+static int parse_diff_unified(char *line, diffhunk_T *hunk)
{
// Parse unified diff hunk header:
// @@ -oldline,oldcount +newline,newcount @@
- char *p = (char *)line;
+ char *p = line;
if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') {
long oldcount;
long newline;
@@ -3163,13 +3162,11 @@ static int parse_diff_unified(char_u *line, diffhunk_T *hunk)
static int xdiff_out(long start_a, long count_a, long start_b, long count_b, void *priv)
{
diffout_T *dout = (diffout_T *)priv;
- diffhunk_T *p = xmalloc(sizeof(*p));
-
- ga_grow(&dout->dout_ga, 1);
- p->lnum_orig = (linenr_T)start_a + 1;
- p->count_orig = count_a;
- p->lnum_new = (linenr_T)start_b + 1;
- p->count_new = count_b;
- ((diffhunk_T **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p;
+ GA_APPEND(diffhunk_T, &(dout->dout_ga), ((diffhunk_T){
+ .lnum_orig = (linenr_T)start_a + 1,
+ .count_orig = count_a,
+ .lnum_new = (linenr_T)start_b + 1,
+ .count_new = count_b,
+ }));
return 0;
}
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index a1582eac53..adf52ef6e4 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -125,6 +125,8 @@ void conceal_check_cursor_line(void)
/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
/// and Columns for positioning text etc. where the final size of the screen is
/// needed.
+///
+/// @return whether resizing has been done
bool default_grid_alloc(void)
{
static bool resizing = false;
@@ -264,17 +266,28 @@ void screen_resize(int width, int height)
p_lines = Rows;
p_columns = Columns;
- // was invoked recursively from a VimResized autocmd, handled as a loop below
- if (resizing_autocmd) {
- return;
- }
+ ui_call_grid_resize(1, width, height);
int retry_count = 0;
resizing_autocmd = true;
- bool retry_resize = true;
- while (retry_resize) {
- retry_resize = default_grid_alloc();
+ // In rare cases, autocommands may have altered Rows or Columns,
+ // so retry to check if we need to allocate the screen again.
+ while (default_grid_alloc()) {
+ // win_new_screensize will recompute floats position, but tell the
+ // compositor to not redraw them yet
+ ui_comp_set_screen_valid(false);
+ if (msg_grid.chars) {
+ msg_grid_invalid = true;
+ }
+
+ RedrawingDisabled++;
+
+ win_new_screensize(); // fit the windows in the new sized screen
+
+ comp_col(); // recompute columns for shown command and ruler
+
+ RedrawingDisabled--;
// Do not apply autocommands more than 3 times to avoid an endless loop
// in case applying autocommands always changes Rows or Columns.
@@ -282,33 +295,10 @@ void screen_resize(int width, int height)
break;
}
- if (retry_resize) {
- // In rare cases, autocommands may have altered Rows or Columns,
- // retry to check if we need to allocate the screen again.
- apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
- }
+ apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
}
resizing_autocmd = false;
-
- ui_call_grid_resize(1, width, height);
-
- // win_new_screensize will recompute floats position, but tell the
- // compositor to not redraw them yet
- ui_comp_set_screen_valid(false);
- if (msg_grid.chars) {
- msg_grid_invalid = true;
- }
-
- // Note that the window sizes are updated before reallocating the arrays,
- // thus we must not redraw here!
- RedrawingDisabled++;
-
- win_new_screensize(); // fit the windows in the new sized screen
-
- comp_col(); // recompute columns for shown command and ruler
-
- RedrawingDisabled--;
redraw_all_later(UPD_CLEAR);
if (starting != NO_SCREEN) {
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
index 9d311fe356..c0c98e0990 100644
--- a/src/nvim/eval/encode.h
+++ b/src/nvim/eval/encode.h
@@ -15,9 +15,7 @@
/// @param[in] objname Object name, used for error message.
///
/// @return OK in case of success, FAIL otherwise.
-int encode_vim_to_msgpack(msgpack_packer *packer,
- typval_T *tv,
- const char *objname);
+int encode_vim_to_msgpack(msgpack_packer *packer, typval_T *tv, const char *objname);
/// Convert VimL value to :echo output
///
@@ -26,9 +24,7 @@ int encode_vim_to_msgpack(msgpack_packer *packer,
/// @param[in] objname Object name, used for error message.
///
/// @return OK in case of success, FAIL otherwise.
-int encode_vim_to_echo(garray_T *packer,
- typval_T *tv,
- const char *objname);
+int encode_vim_to_echo(garray_T *packer, typval_T *tv, const char *objname);
/// Structure defining state for read_from_list()
typedef struct {
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index f6530d0e92..4a62b4bf8d 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -1877,6 +1877,36 @@ theend:
#define MAX_FUNC_NESTING 50
+/// List functions.
+///
+/// @param regmatch When NULL, all of them.
+/// Otherwise functions matching "regmatch".
+static void list_functions(regmatch_T *regmatch)
+{
+ const size_t used = func_hashtab.ht_used;
+ size_t todo = used;
+ const hashitem_T *const ht_array = func_hashtab.ht_array;
+
+ for (const hashitem_T *hi = ht_array; todo > 0 && !got_int; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ ufunc_T *fp = HI2UF(hi);
+ todo--;
+ if ((fp->uf_flags & FC_DEAD) == 0
+ && (regmatch == NULL
+ ? (!message_filtered((char *)fp->uf_name)
+ && !func_name_refcount(fp->uf_name))
+ : (!isdigit(*fp->uf_name)
+ && vim_regexec(regmatch, (char *)fp->uf_name, 0)))) {
+ list_func_head(fp, false, false);
+ if (used != func_hashtab.ht_used || ht_array != func_hashtab.ht_array) {
+ emsg(_("E454: function list was modified"));
+ return;
+ }
+ }
+ }
+ }
+}
+
/// ":function"
void ex_function(exarg_T *eap)
{
@@ -1903,7 +1933,6 @@ void ex_function(exarg_T *eap)
static int func_nr = 0; // number for nameless function
int paren;
hashtab_T *ht;
- int todo;
hashitem_T *hi;
linenr_T sourcing_lnum_off;
linenr_T sourcing_lnum_top;
@@ -1916,19 +1945,7 @@ void ex_function(exarg_T *eap)
// ":function" without argument: list functions.
if (ends_excmd(*eap->arg)) {
if (!eap->skip) {
- todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- fp = HI2UF(hi);
- if (message_filtered((char *)fp->uf_name)) {
- continue;
- }
- if (!func_name_refcount(fp->uf_name)) {
- list_func_head(fp, false, false);
- }
- }
- }
+ list_functions(NULL);
}
eap->nextcmd = check_nextcmd(eap->arg);
return;
@@ -1946,18 +1963,7 @@ void ex_function(exarg_T *eap)
*p = c;
if (regmatch.regprog != NULL) {
regmatch.rm_ic = p_ic;
-
- todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- fp = HI2UF(hi);
- if (!isdigit(*fp->uf_name)
- && vim_regexec(&regmatch, (char *)fp->uf_name, 0)) {
- list_func_head(fp, false, false);
- }
- }
- }
+ list_functions(&regmatch);
vim_regfree(regmatch.regprog);
}
}
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 71956b2246..0015a82880 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -62,7 +62,7 @@
#define EX_FLAGS 0x200000u // allow flags after count in argument
#define EX_LOCK_OK 0x1000000u // command can be executed when textlock is
// set; when missing disallows editing another
- // buffer when current buffer is locked
+ // buffer when curbuf->b_ro_locked is set
#define EX_KEEPSCRIPT 0x4000000u // keep sctx of where command was invoked
#define EX_PREVIEW 0x8000000u // allow incremental command preview
#define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 0fd65e8f5a..443a217143 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -550,7 +550,7 @@ func Test_argdo()
bwipe Xa.c Xb.c Xc.c
endfunc
-" Test for quiting Vim with unedited files in the argument list
+" Test for quitting Vim with unedited files in the argument list
func Test_quit_with_arglist()
if !CanRunVimInTerminal()
throw 'Skipped: cannot run vim in terminal'
diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim
index 2284704603..aaed77e109 100644
--- a/src/nvim/testdir/test_langmap.vim
+++ b/src/nvim/testdir/test_langmap.vim
@@ -52,7 +52,7 @@ func Test_langmap()
set langmap=RL
let g:counter = 0
nnoremap L;L <Cmd>let g:counter += 1<CR>
- nnoremap <C-L> <Cmd>throw 'This mapping shoud not be triggered'<CR>
+ nnoremap <C-L> <Cmd>throw 'This mapping should not be triggered'<CR>
" 'langmap' is applied to keys without modifiers when matching a mapping
call feedkeys('R;R', 'tx')
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index db7ec92bf8..2e149ad5a5 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -429,7 +429,7 @@ func Test_menu_special()
nunmenu Test.Sign
endfunc
-" Test for "icon=filname" in a toolbar
+" Test for "icon=filename" in a toolbar
func Test_menu_icon()
CheckFeature toolbar
nmenu icon=myicon.xpm Toolbar.Foo :echo "Foo"<CR>
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index b0e83b7f5f..dcedfe26a2 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -3251,7 +3251,7 @@ func Test_cclose_in_autocmd()
" call test_override('starting', 0)
endfunc
-" Check that ":file" without an argument is possible even when curbuf is locked
+" Check that ":file" without an argument is possible even when "curbuf->b_ro_locked"
" is set.
func Test_file_from_copen()
" Works without argument.
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 4d450e180b..771f61442d 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -3,6 +3,7 @@
source check.vim
CheckFeature timers
+source screendump.vim
source shared.vim
source term_util.vim
source load.vim
@@ -408,6 +409,30 @@ func Test_timer_invalid_callback()
call assert_fails('call timer_start(0, "0")', 'E921')
endfunc
+func Test_timer_changing_function_list()
+ CheckRunVimInTerminal
+
+ " Create a large number of functions. Should get the "more" prompt.
+ " The typing "G" triggers the timer, which changes the function table.
+ let lines =<< trim END
+ for func in map(range(1,99), "'Func' .. v:val")
+ exe "func " .. func .. "()"
+ endfunc
+ endfor
+ au CmdlineLeave : call timer_start(0, {-> 0})
+ END
+ call writefile(lines, 'XTest_timerchange')
+ let buf = RunVimInTerminal('-S XTest_timerchange', #{rows: 10})
+ call term_sendkeys(buf, ":fu\<CR>")
+ call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 10))})
+ call term_sendkeys(buf, "G")
+ call WaitForAssert({-> assert_match('E454', term_getline(buf, 9))})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+ call delete('XTest_timerchange')
+endfunc
+
func Test_timer_using_win_execute_undo_sync()
let bufnr1 = bufnr()
new
diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim
index 0acd7fc1e5..32ad64cda4 100644
--- a/src/nvim/testdir/test_vartabs.vim
+++ b/src/nvim/testdir/test_vartabs.vim
@@ -97,7 +97,7 @@ func Test_vartabs()
.retab!
call assert_equal("\t\t\t\tl", getline(1))
- " Test for 'retab' with same vlaues as vts
+ " Test for 'retab' with same values as vts
set ts=8 sts=0 vts=5,3,6,2 vsts=
exe "norm! S l"
.retab! 5,3,6,2
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 7fb34ec81f..a9c057088d 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -1281,7 +1281,7 @@ func Test_visual_block_with_virtualedit()
endfunc
func Test_visual_block_ctrl_w_f()
- " Emtpy block selected in new buffer should not result in an error.
+ " Empty block selected in new buffer should not result in an error.
au! BufNew foo sil norm f
edit foo
diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h
index 9d0bc9d468..77fbfa615f 100644
--- a/src/nvim/viml/parser/expressions.h
+++ b/src/nvim/viml/parser/expressions.h
@@ -357,7 +357,7 @@ typedef struct {
int arg_len;
} ExprASTError;
-/// Structure representing complety AST for one expression
+/// Structure representing complete AST for one expression
typedef struct {
/// When AST is not correct this message will be printed.
///
diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h
index 5e1cf9c6d8..56e8b2d32b 100644
--- a/src/nvim/viml/parser/parser.h
+++ b/src/nvim/viml/parser/parser.h
@@ -127,8 +127,7 @@ static inline void viml_parser_destroy(ParserState *const pstate)
kvi_destroy(pstate->stack);
}
-static inline void viml_preader_get_line(ParserInputReader *preader,
- ParserLine *ret_pline)
+static inline void viml_preader_get_line(ParserInputReader *preader, ParserLine *ret_pline)
REAL_FATTR_NONNULL_ALL;
/// Get one line from ParserInputReader
@@ -152,8 +151,7 @@ static inline void viml_preader_get_line(ParserInputReader *const preader,
*ret_pline = pline;
}
-static inline bool viml_parser_get_remaining_line(ParserState *pstate,
- ParserLine *ret_pline)
+static inline bool viml_parser_get_remaining_line(ParserState *pstate, ParserLine *ret_pline)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
/// Get currently parsed line, shifted to pstate->pos.col
@@ -178,8 +176,7 @@ static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
return ret_pline->data != NULL;
}
-static inline void viml_parser_advance(ParserState *pstate,
- size_t len)
+static inline void viml_parser_advance(ParserState *pstate, size_t len)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
/// Advance position by a given number of bytes
@@ -200,9 +197,7 @@ static inline void viml_parser_advance(ParserState *const pstate, const size_t l
}
}
-static inline void viml_parser_highlight(ParserState *pstate,
- ParserPosition start,
- size_t len,
+static inline void viml_parser_highlight(ParserState *pstate, ParserPosition start, size_t len,
const char *group)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index f250a3ec93..38f772c5cf 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -430,6 +430,8 @@ describe('lua stdlib', function()
it('vim.pesc', function()
eq('foo%-bar', exec_lua([[return vim.pesc('foo-bar')]]))
eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]]))
+ -- pesc() returns one result. #20751
+ eq({'x'}, exec_lua([[return {vim.pesc('x')}]]))
-- Validates args.
matches('s: expected string, got number',
@@ -1016,11 +1018,11 @@ describe('lua stdlib', function()
eq('hi', funcs.luaeval "vim.g.testing")
eq(123, funcs.luaeval "vim.g.other")
eq(5120.1, funcs.luaeval "vim.g.floaty")
- eq(NIL, funcs.luaeval "vim.g.nonexistant")
+ eq(NIL, funcs.luaeval "vim.g.nonexistent")
eq(NIL, funcs.luaeval "vim.g.nullvar")
-- lost over RPC, so test locally:
eq({false, true}, exec_lua [[
- return {vim.g.nonexistant == vim.NIL, vim.g.nullvar == vim.NIL}
+ return {vim.g.nonexistent == vim.NIL, vim.g.nullvar == vim.NIL}
]])
eq({hello="world"}, funcs.luaeval "vim.g.to_delete")
@@ -1123,12 +1125,12 @@ describe('lua stdlib', function()
eq('bye', funcs.luaeval "vim.b[BUF].testing")
eq(123, funcs.luaeval "vim.b.other")
eq(5120.1, funcs.luaeval "vim.b.floaty")
- eq(NIL, funcs.luaeval "vim.b.nonexistant")
- eq(NIL, funcs.luaeval "vim.b[BUF].nonexistant")
+ eq(NIL, funcs.luaeval "vim.b.nonexistent")
+ eq(NIL, funcs.luaeval "vim.b[BUF].nonexistent")
eq(NIL, funcs.luaeval "vim.b.nullvar")
-- lost over RPC, so test locally:
eq({false, true}, exec_lua [[
- return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL}
+ return {vim.b.nonexistent == vim.NIL, vim.b.nullvar == vim.NIL}
]])
matches([[attempt to index .* nil value]],
@@ -1207,7 +1209,7 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.b.testing")
eq(NIL, funcs.luaeval "vim.b.other")
- eq(NIL, funcs.luaeval "vim.b.nonexistant")
+ eq(NIL, funcs.luaeval "vim.b.nonexistent")
end)
it('vim.w', function()
@@ -1226,8 +1228,8 @@ describe('lua stdlib', function()
eq('hi', funcs.luaeval "vim.w.testing")
eq('bye', funcs.luaeval "vim.w[WIN].testing")
eq(123, funcs.luaeval "vim.w.other")
- eq(NIL, funcs.luaeval "vim.w.nonexistant")
- eq(NIL, funcs.luaeval "vim.w[WIN].nonexistant")
+ eq(NIL, funcs.luaeval "vim.w.nonexistent")
+ eq(NIL, funcs.luaeval "vim.w[WIN].nonexistent")
matches([[attempt to index .* nil value]],
pcall_err(exec_lua, 'return vim.w[WIN][0].testing'))
@@ -1305,7 +1307,7 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.w.testing")
eq(NIL, funcs.luaeval "vim.w.other")
- eq(NIL, funcs.luaeval "vim.w.nonexistant")
+ eq(NIL, funcs.luaeval "vim.w.nonexistent")
end)
it('vim.t', function()
@@ -1317,10 +1319,10 @@ describe('lua stdlib', function()
eq('hi', funcs.luaeval "vim.t.testing")
eq(123, funcs.luaeval "vim.t.other")
- eq(NIL, funcs.luaeval "vim.t.nonexistant")
+ eq(NIL, funcs.luaeval "vim.t.nonexistent")
eq('hi', funcs.luaeval "vim.t[0].testing")
eq(123, funcs.luaeval "vim.t[0].other")
- eq(NIL, funcs.luaeval "vim.t[0].nonexistant")
+ eq(NIL, funcs.luaeval "vim.t[0].nonexistent")
matches([[attempt to index .* nil value]],
pcall_err(exec_lua, 'return vim.t[0][0].testing'))
@@ -1387,7 +1389,7 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.t.testing")
eq(NIL, funcs.luaeval "vim.t.other")
- eq(NIL, funcs.luaeval "vim.t.nonexistant")
+ eq(NIL, funcs.luaeval "vim.t.nonexistent")
end)
it('vim.env', function()
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index e9072ebf98..5b62f5b3e1 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -1115,7 +1115,7 @@ if (h->n_buckets < new_n_buckets) { // expand
]]}
meths.buf_set_extmark(0, ns, 5, 0, {
- virt_lines = { {{"^^ REVIEW:", "Todo"}, {" new_vals variable seems unneccesary?", "Comment"}} };
+ virt_lines = { {{"^^ REVIEW:", "Todo"}, {" new_vals variable seems unnecessary?", "Comment"}} };
})
-- TODO: what about the cursor??
screen:expect{grid=[[
@@ -1128,7 +1128,7 @@ if (h->n_buckets < new_n_buckets) { // expand
if (kh_is_map && val_size) { |
^char *new_vals = {3:krealloc}( h->vals_buf, new_n_|
buckets * val_size); |
- {5:^^ REVIEW:}{6: new_vals variable seems unneccesary?} |
+ {5:^^ REVIEW:}{6: new_vals variable seems unnecessary?} |
h->vals_buf = new_vals; |
|
]]}
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index f111aa2513..5aacdc95e2 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -894,6 +894,31 @@ local function screen_tests(linegrid)
:ls^ |
]])
end)
+
+ it('VimResized autocommand does not cause invalid UI events #20692 #20759', function()
+ feed('<Esc>')
+ command([[autocmd VimResized * redrawtabline]])
+ command([[autocmd VimResized * lua vim.api.nvim_echo({ { 'Hello' } }, false, {})]])
+ command([[autocmd VimResized * let g:echospace = v:echospace]])
+ meths.set_option('showtabline', 2)
+ screen:expect([[
+ {2: + [No Name] }{3: }|
+ resiz^e |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ screen:try_resize(30, 6)
+ screen:expect([[
+ {2: + [No Name] }{3: }|
+ resiz^e |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ eq(29, meths.get_var('echospace'))
+ end)
end)
describe('press enter', function()
diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua
index 1fbdedb815..64a3cf5471 100644
--- a/test/functional/vimscript/eval_spec.lua
+++ b/test/functional/vimscript/eval_spec.lua
@@ -17,12 +17,16 @@ local clear = helpers.clear
local eq = helpers.eq
local exc_exec = helpers.exc_exec
local exec = helpers.exec
+local exec_lua = helpers.exec_lua
local exec_capture = helpers.exec_capture
local eval = helpers.eval
local command = helpers.command
local write_file = helpers.write_file
local meths = helpers.meths
local sleep = helpers.sleep
+local matches = helpers.matches
+local pcall_err = helpers.pcall_err
+local assert_alive = helpers.assert_alive
local poke_eventloop = helpers.poke_eventloop
local feed = helpers.feed
@@ -249,10 +253,10 @@ describe("uncaught exception", function()
end)
end)
-describe('lambda function', function()
+describe('listing functions using :function', function()
before_each(clear)
- it('can be shown using :function followed by <lambda> #20466', function()
+ it('works for lambda functions with <lambda> #20466', function()
command('let A = {-> 1}')
local num = exec_capture('echo A'):match("function%('<lambda>(%d+)'%)")
eq(([[
@@ -260,4 +264,33 @@ describe('lambda function', function()
1 return 1
endfunction]]):format(num), exec_capture(('function <lambda>%s'):format(num)))
end)
+
+ -- FIXME: If the same function is deleted, the crash still happens. #20790
+ it('does not crash if another function is deleted while listing', function()
+ local screen = Screen.new(80, 24)
+ screen:attach()
+ matches('.*: Vim%(function%):E454: function list was modified', pcall_err(exec_lua, [=[
+ vim.cmd([[
+ func Func1()
+ endfunc
+ func Func2()
+ endfunc
+ func Func3()
+ endfunc
+ ]])
+
+ local ns = vim.api.nvim_create_namespace('test')
+
+ vim.ui_attach(ns, { ext_messages = true }, function(event, _, content)
+ if event == 'msg_show' and content[1][2] == 'function Func1()' then
+ vim.cmd('delfunc Func3')
+ end
+ end)
+
+ vim.cmd('function')
+
+ vim.ui_detach(ns)
+ ]=]))
+ assert_alive()
+ end)
end)