Problem: With $d='[dir]', `:e $d/file.txt` opens the wrong file,
`:e $d/<Tab>` fails to complete, and `glob('$d/*')` returns
nothing. Wildcard characters inside expanded environment
variables get picked up by globbing again.
Solution: Turn the 4th parameter of expand_env_esc() from a bool into a
string of characters to escape in each expanded value. Callers
that pass the result to wildcard expansion should include
PATH_ESC_WILDCARDS in addition to " \t" (glepnir).
closes: #20053
Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Several win_T fields are declared as "int" or "char" but are
used strictly as boolean flags with TRUE/FALSE values. The
integer types obscure the boolean intent and are wider than
needed.
Solution: Change the following win_T members to bool (stdbool.h) and
update their assignments from TRUE/FALSE to true/false
accordingly.
The following conversions have been done:
- int -> bool (10 members):
w_set_curswant, w_botfill, w_old_botfill, w_do_win_fix_cursor,
w_popup_fixed, w_border_highlight_isset, w_cline_folded,
w_redr_status, w_arg_idx_invalid, w_has_scrollbar
- char -> bool (4 members):
w_topline_was_set, w_ru_empty, w_fold_manual, w_foldinvalid
No existing code compares these members against TRUE/FALSE explicitly or
uses ++/-- / bitwise ops on them, so only plain assignments are
affected.
Excluded:
- w_locked (recursion counter with ++/--),
- w_want_scrollbar (may hold -1 from dict_get_bool),
- w_winbar_height (used in arithmetic and exposed as number via
getwininfo()).
related: #20005
closes: #20008
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Several gui_T fields are declared as "int" or "char" but are
used strictly as boolean flags with TRUE/FALSE values. The
integer types obscure the boolean intent and are wider than
needed.
Solution: Change the following gui_T members to bool (stdbool.h) and
update their assignments from TRUE/FALSE to true/false
accordingly (Hirohito Higashi)
The following conversions have been done:
- int -> bool (11 members):
in_focus, in_use, starting, dying, dofork, dospawn,
pointer_hidden, force_redraw, directx_enabled, font_can_bold,
which_scrollbars[3]
- char -> bool (2 members):
cursor_is_valid, menu_is_active
No existing code compares these members against TRUE/FALSE explicitly
(e.g. "== TRUE"), so only plain assignments are affected.
gui_init() used counter-style "--gui.starting" / "++gui.starting" to
temporarily clear the flag across a call to gui_mch_enable_menu().
With gui.starting now bool this triggers -Werror=bool-operation, so
replace it with an explicit save/restore.
X11 Bool members (rsrc_rev_video, color_approx) are intentionally left
unchanged: rsrc_rev_video is registered as an X Toolkit resource with
XtRBool / sizeof(Bool) and must keep the int-sized X11 Bool type.
closes: #20005
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: too many strlen() calls
Solution: refactor concat_fname() and remove calls to strlen()
(John Marriott)
Function `concat_fnames()` can make up to 5 calls to `STRLEN()` (either
directly or indirectly via `STRCAT()`). In many cases the lengths of
arguments `fname1` and/or `fname2` are either known or can simply be
calculated.
This Commit refactors this function to accept the lengths of arguments
`fname1` and `fname2` as arguments. It also adds new argument `ret` to
return the resulting string as a `string_T`.
Additionally:
- function `add_pack_dir_to_rtp()` in `scriptfile.c`:
Use a `string_T` to store local variables `new_rtp` and `afterdir`.
Replace calls to `STRCAT()` with calls to `STRCPY()`.
Change type of variable `keep` to `size_t` for consistency with
other lengths.
- function `qf_get_fnum()` in `quickfix.c`:
Use a `string_T` to store local variables `ptr` and `bufname`
- function `qf_push_dir()` in `quickfix.c`:
Use a `string_T` to store local variable `dirname`.
Replace call to `vim_strsave()` with `vim_strnsave()`.
- function `qf_guess_filepath()` in `quickfix.c`:
Use a `string_T` to store local variable `fullname`.
- function `make_percent_swname()` in `memline.c`:
Rename some variables to better reflect their use.
Use a `string_T` to store local variables `d` and `fixed_name`.
Slightly refactor to remove need to create an extra string.
- function `get_file_in_dir()` in `memline.c`:
Use a `string_T` to store local variables `tail` and `retval`.
Move some variables closer to where they are used.
- function `cs_resolve_file()` in `if_cscope.c`:
Use a `string_T` to store local variable `csdir`.
Remove one call to `STRLEN()`.
- function `add_pathsep()` in `filepath.c`:
Refactor and remove 1 call to `STRLEN()`
- function `set_init_xdg_rtp()` in `option.c`:
Use a `string_T` to store local variable `vimrc_xdg`.
closes: #19854
Co-authored-by: Christian Brabandt <cb@256bit.org>
Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: 'linebreak' may lead to wrong Visual block highlighting when
end char occupies multiple cells (after 7.4.467).
Solution: Exclude 'linebreak' from the ending column instead of setting
'virtualedit' temporarily (zeertzjq).
fixes: #19898closes: #19900
Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: system() and systemlist() only accept a String, requiring
manual shell escaping for arguments with special characters.
Solution: Accept a List as the first argument and execute the command
bypassing the shell (Yasuhiro Matsumoto).
fixes: #19789closes: #19791
Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: line('w0') moves cursor in terminal-normal mode
(Biebar, after v9.2.0127)
Solution: Check that the terminal is not in terminal-normal-mode
(Ozaki Kiichi).
fixes: #19717closes: #19718
Signed-off-by: Ozaki Kiichi <gclient.gaap@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: VIM_BACKTICK is always defined except for tiny builds
Solution: Remove the VIM_BACKTICK define and always compile backtick
expansion. This change however will cause the vim.tiny build
to be slightly larger (Martin Tournoij).
closes: #19562
Signed-off-by: Martin Tournoij <martin@arp242.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: tabpanel: inconsistent use of cmdline and message area with
tabpanel
Solution: Reduce the cmdline and message area by the horizontal size of
the tabpanel (Hirohito Higashi)
closes: #18678
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Ctrl-F and Ctrl-B at more prompt not working with kitty
keyboard protocol or modifyOtherKeys (after v9.1.1849).
Solution: Call merge_modifyOtherKeys() in get_keystroke() (zeertzjq).
closes: #18558
Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Generating prototype files does not work on all platforms
Solution: Rework prototypes generation using python instead of cproto,
enable it in CI to test it for each PR (Hirohito Higashi).
closes: #18045
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: cannot perform autocompletion
Solution: Add the 'autocomplete' option value
(Girish Palya)
This change introduces the 'autocomplete' ('ac') boolean option to
enable automatic popup menu completion during insert mode. When enabled,
Vim shows a completion menu as you type, similar to pressing |i\_CTRL-N|
manually. The items are collected from sources defined in the
'complete' option.
To ensure responsiveness, this feature uses a time-sliced strategy:
- Sources earlier in the 'complete' list are given more time.
- If a source exceeds its allocated timeout, it is interrupted.
- The next source is then started with a reduced timeout (exponentially
decayed).
- A small minimum ensures every source still gets a brief chance to
contribute.
The feature is fully compatible with other |i_CTRL-X| completion modes,
which can temporarily suspend automatic completion when triggered.
See :help 'autocomplete' and :help ins-autocompletion for more details.
To try it out, use :set ac
You should see a popup menu appear automatically with suggestions. This
works seamlessly across:
- Large files (multi-gigabyte size)
- Massive codebases (:argadd thousands of .c or .h files)
- Large dictionaries via the `k` option
- Slow or blocking LSP servers or user-defined 'completefunc'
Despite potential slowness in sources, the menu remains fast,
responsive, and useful.
Compatibility: This mode is fully compatible with existing completion
methods. You can still invoke any CTRL-X based completion (e.g.,
CTRL-X CTRL-F for filenames) at any time (CTRL-X temporarily
suspends 'autocomplete'). To specifically use i_CTRL-N, dismiss the
current popup by pressing CTRL-E first.
---
How it works
To keep completion snappy under all conditions, autocompletion uses a
decaying time-sliced algorithm:
- Starts with an initial timeout (80ms).
- If a source does not complete within the timeout, it's interrupted and
the timeout is halved for the next source.
- This continues recursively until a minimum timeout (5ms) is reached.
- All sources are given a chance, but slower ones are de-prioritized
quickly.
Most of the time, matches are computed well within the initial window.
---
Implementation details
- Completion logic is mostly triggered in `edit.c` and handled in
insexpand.c.
- Uses existing inc_compl_check_keys() mechanism, so no new polling
hooks are needed.
- The completion system already checks for user input periodically; it
now also checks for timer expiry.
---
Design notes
- The menu doesn't continuously update after it's shown to prevent
visual distraction (due to resizing) and ensure the internal list
stays synchronized with the displayed menu.
- The 'complete' option determines priority—sources listed earlier get
more time.
- The exponential time-decay mechanism prevents indefinite collection,
contributing to low CPU usage and a minimal memory footprint.
- Timeout values are intentionally not configurable—this system is
optimized to "just work" out of the box. If autocompletion feels slow,
it typically indicates a deeper performance bottleneck (e.g., a slow
custom function not using `complete_check()`) rather than a
configuration issue.
---
Performance
Based on testing, the total roundtrip time for completion is generally
under 200ms. For common usage, it often responds in under 50ms on an
average laptop, which falls within the "feels instantaneous" category
(sub-100ms) for perceived user experience.
| Upper Bound (ms) | Perceived UX
|----------------- |-------------
| <100 ms | Excellent; instantaneous
| <200 ms | Good; snappy
| >300 ms | Noticeable lag
| >500 ms | Sluggish/Broken
---
Why this belongs in core:
- Minimal and focused implementation, tightly integrated with existing
Insert-mode completion logic.
- Zero reliance on autocommands and external scripting.
- Makes full use of Vim’s highly composable 'complete' infrastructure
while avoiding the complexity of plugin-based solutions.
- Gives users C native autocompletion with excellent responsiveness and
no configuration overhead.
- Adds a key UX functionality in a simple, performant, and Vim-like way.
closes: #17812
Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: too many strlen() calls
Solution: Change expand_env() to return string length
(John Marriott)
This commit does the following changes:
- In expand_env_esc():
- return the length of the returned dst string.
- refactor to remove some calls to STRLEN() and STRCAT()
- add check for out-of-memory condition.
- Change call sites in various source files to use the return value
closes: #17561
Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: [security]: heap-buffer-overflow with visual mode when
using :all, causing Vim trying to access beyond end-of-line
(gandalf)
Solution: Reset visual mode on :all, validate position in gchar_pos()
and charwise_block_prep()
This fixes CVE-2025-22134
Github Advisory:
https://github.com/vim/vim/security/advisories/GHSA-5rgf-26wj-48v8
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: some global functions are only used in single files
Solution: refactor code slightly and make some more functions static
(Yegappan Lakshmanan)
closes: #15951
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: mode() returns wrong value with <Cmd> mapping
Solution: Change decision priority of VIsual_active and move
visual mode a bit further down (kuuote)
closes: #15533
Signed-off-by: kuuote <znmxodq1@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Calculating line height for unnecessary amount of lines with
half-page scrolling (zhscn, after 9.1.0280)
Solution: Replace "limit_winheight" argument with higher resolution
"max" argument to which to limit the calculated line height
in plines_m_win() to (Luuk van Baal)
fixes: #14650closes: #14652
Signed-off-by: Luuk van Baal <luukvbaal@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: plines_m_win() does not take into account it's "limit_winheight"
argument for filler lines below the last line of the buffer.
(after v9.1.0280)
Solution: Check window height when "limit_winheight" is TRUE.
(Luuk van Baal)
closes: #14449
Signed-off-by: Luuk van Baal <luukvbaal@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Logic to make sure cursor is in visible part of the screen after
scrolling the text with 'smoothscroll' is scattered, asymmetric
and contains bugs.
Solution: Adjust and create helper function for 'smoothscroll' cursor logic.
(Luuk van Baal)
closes: #14410
Signed-off-by: Luuk van Baal <luukvbaal@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: leaking memory in add_user() (LuMingYinDetect)
Solution: free user_copy pointer instead of the user ptr
add_user() is called with a user pointer and the user pointer comes
from these functions:
- return value from the getpwent() function (Unix).
- return value from the getpwnam() function (Unix).
- return value from the NetUserEnum() function (MS Windows).
For the first 2 callers, the man page for those functions directly says,
one should not free the returned pointer (since it comes from static
memory).
For the third caller (on MS Windows), the returned buffer is already
freed directly after calling the add_user() function in
NetApiBufferFree(), so this would lead to a double free().
This all indicates, the user ptr is wrongly freed in the add_user()
function and the intention was to free the user_copy pointer instead in
case of an error.
So let's just use that now.
fixes: #14250closes: #14260
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: expand() removes a slash after an environment variable that
ends with a colon on Windows.
Solution: Check the correct char for a colon (zeertzjq)
closes: #14161
Note: Vim still removes the path-separator at the end, if another path separator
follows directly after it, e.g. on:
```
echo $FOO='/usr/'
echo expand('$FOO/bar') == '/usr/bar'
```
see:
,----[ misc1.c:1630 ]
| // if var[] ends in a path separator and tail[] starts
| // with it, skip a character
| if (after_pathsep(dst, dst + c)
| #if defined(BACKSLASH_IN_FILENAME) || defined(AMIGA)
| && (dst == save_dst || dst[-1] != ':')
| #endif
| && vim_ispathsep(*tail))
| ++tail;
`----
Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Overflow logic requires long long
Solution: Define vimlong_T data type to make life easier
for porters
closes: #13598
Signed-off-by: Ernie Rael <errael@raelity.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: INT overflow logic can be simplified
Solution: introduce trim_to_int() function
closes: #13556
Signed-off-by: Ernie Rael <errael@raelity.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: overflow detection not accurate when adding digits
Solution: Use a helper function
Use a helper function to better detect overflows before adding integer
digits to a long or an integer variable respectively. Signal the
overflow to the caller function.
closes: #13539
Signed-off-by: Christian Brabandt <cb@256bit.org>
Signed-off-by: Michael Henry <vim@drmikehenry.com>
Signed-off-by: Ernie Rael <errael@raelity.com>
Problem: [security]: overflow in get_number
Solution: Return 0 when the count gets too large
[security]: overflow in get_number
When using the z= command, we may overflow the count with values larger
than MAX_INT. So verify that we do not overflow and in case when an
overflow is detected, simply return 0
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: mode() doesn't indicate command line for terminal
Solution: make it return 'ct' for command-line from Terminal mode
closes: #6265closes: #13017closes: #13018
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: h-east <h.east.727@gmail.com>
Problem: screenpos() wrong result with w_skipcol and cpoptions+=n
Solution: Use adjust_plines_for_skipcol() instead of subtracting
w_skipcol.
closes: #12625
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Problem: Using isalpha() adds dependency on current locale.
Solution: Do not use isalpha() for recognizing a URL or the end of an Ex
command. (closes#11835)
Problem: ModeChanged autocmd not executed when Visual mode is ended with
CTRL-C.
Solution: Do not trigger the autocmd when got_int is set. (closes#11394)
Problem: Mechanism to prevent recursive screen updating is incomplete.
Solution: Add "redraw_not_allowed" and set it in build_stl_str_hl().
(issue #10952)