diff options
60 files changed, 4419 insertions, 615 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index d9067b05e3..530095c40f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -10,7 +10,7 @@ freebsd_task: timeout_in: 30m install_script: - pkg update -f - - pkg install -y cmake gmake ninja pkgconf unzip wget gettext python libffi git + - pkg install -y cmake gmake ninja unzip wget gettext python git build_deps_script: - gmake deps build_script: diff --git a/.github/scripts/install_deps.sh b/.github/scripts/install_deps.sh index bb99873267..32591eb8da 100755 --- a/.github/scripts/install_deps.sh +++ b/.github/scripts/install_deps.sh @@ -18,7 +18,7 @@ done os=$(uname -s) if [[ $os == Linux ]]; then $SUDO apt-get update - $SUDO apt-get install -y build-essential cmake curl gettext ninja-build pkg-config unzip + $SUDO apt-get install -y build-essential cmake curl gettext ninja-build unzip if [[ -n $TEST ]]; then $SUDO apt-get install -y locales-all cpanminus fi diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt index 20cb8f31f6..3d835064ba 100644 --- a/cmake.deps/CMakeLists.txt +++ b/cmake.deps/CMakeLists.txt @@ -135,81 +135,23 @@ endif() include(ExternalProject) set_directory_properties(PROPERTIES EP_PREFIX "${DEPS_BUILD_DIR}") -set(LIBUV_URL https://github.com/libuv/libuv/archive/62c2374a8c005ce9e42088965f8f8af2532c177b.tar.gz) -set(LIBUV_SHA256 c7e89137da65a1cb550ba96b892dfeeabea982bf33b9237bcf9bbcd90f2e70a1) - -set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/c-6.0.0/msgpack-c-6.0.0.tar.gz) -set(MSGPACK_SHA256 3654f5e2c652dc52e0a993e270bb57d5702b262703f03771c152bba51602aeba) - -# https://github.com/LuaJIT/LuaJIT/tree/v2.1 -set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/224129a8e64bfa219d35cd03055bf03952f167f6.tar.gz) -set(LUAJIT_SHA256 a9bcd9e646e2b188e1d7e3fb594e04c61dda3b332dfd0378d41be19c1eae9d09) - -set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz) -set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333) - -set(LUAROCKS_URL https://github.com/luarocks/luarocks/archive/v3.9.2.tar.gz) -set(LUAROCKS_SHA256 a0b36cd68586cd79966d0106bb2e5a4f5523327867995fd66bee4237062b3e3b) - -set(UNIBILIUM_URL https://github.com/neovim/unibilium/archive/d72c3598e7ac5d1ebf86ee268b8b4ed95c0fa628.tar.gz) -set(UNIBILIUM_SHA256 9c4747c862ab5e3076dcf8fa8f0ea7a6b50f20ec5905618b9536655596797487) - -set(LIBTERMKEY_URL https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/libtermkey/0.22-1/libtermkey_0.22.orig.tar.gz) -set(LIBTERMKEY_SHA256 6945bd3c4aaa83da83d80a045c5563da4edd7d0374c62c0d35aec09eb3014600) - -set(LIBVTERM_URL https://launchpad.net/libvterm/trunk/v0.3/+download/libvterm-0.3.1.tar.gz) -set(LIBVTERM_SHA256 25a8ad9c15485368dfd0a8a9dca1aec8fea5c27da3fa74ec518d5d3787f0c397) - -set(LUV_URL https://github.com/luvit/luv/archive/093a977b82077591baefe1e880d37dfa2730bd54.tar.gz) -set(LUV_SHA256 222b38b6425f0926218e14e7da81481fdde6f9660c1feac25a53e6fb52e886e6) - -set(LPEG_URL http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.2.tar.gz) -set(LPEG_SHA256 48d66576051b6c78388faad09b70493093264588fcd0f258ddaab1cdd4a15ffe) - -set(LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.tar.gz) -set(LUA_COMPAT53_SHA256 ad05540d2d96a48725bb79a1def35cf6652a4e2ec26376e2617c8ce2baa6f416) - -# Windows only: cat.exe tee.exe xxd.exe -set(CAT_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/cat.exe) -set(CAT_SHA256 93b8d307bb15af3968920bdea3beb869a49d166f9164853c58a4e6ffdcae61c6) -set(TEE_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/tee.exe) -set(TEE_SHA256 950eea4e17fa3a7e89fa2c55374037b5797b3f1a54fea1304634884ab42ec14d) -set(XXD_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/xxd.exe) -set(XXD_SHA256 7a581e3882d28161cc52850f9a11d634b3eaf2c029276f093c1ed4c90e45a10c) - -set(WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.17/neovim-qt.zip) -set(WINGUI_SHA256 502e386eef677c2c2e0c11d8cbb27f3e12b4d96818369417e8da4129c4580c25) - -set(WIN32YANK_X86_64_URL https://github.com/equalsraf/win32yank/releases/download/v0.1.1/win32yank-x64.zip) -set(WIN32YANK_X86_64_SHA256 247c9a05b94387a884b49d3db13f806b1677dfc38020f955f719be6902260cd6) - -set(GETTEXT_URL https://ftp.gnu.org/pub/gnu/gettext/gettext-0.20.1.tar.gz) -set(GETTEXT_SHA256 66415634c6e8c3fa8b71362879ec7575e27da43da562c798a8a2f223e6e47f5c) - -set(LIBICONV_URL https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz) -set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178) - -set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.2.tar.gz) -set(TREESITTER_C_SHA256 af66fde03feb0df4faf03750102a0d265b007e5d957057b6b293c13116a70af2 ) -set(TREESITTER_LUA_URL https://github.com/MunifTanjim/tree-sitter-lua/archive/v0.0.14.tar.gz) -set(TREESITTER_LUA_SHA256 930d0370dc15b66389869355c8e14305b9ba7aafd36edbfdb468c8023395016d) -set(TREESITTER_VIM_URL https://github.com/neovim/tree-sitter-vim/archive/v0.3.0.tar.gz) -set(TREESITTER_VIM_SHA256 403acec3efb7cdb18ff3d68640fc823502a4ffcdfbb71cec3f98aa786c21cbe2) -set(TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v2.0.0.tar.gz) -set(TREESITTER_VIMDOC_SHA256 1ff8f4afd3a9599dd4c3ce87c155660b078c1229704d1a254433e33794b8f274) -set(TREESITTER_QUERY_URL https://github.com/nvim-treesitter/tree-sitter-query/archive/v0.1.0.tar.gz) -set(TREESITTER_QUERY_SHA256 e2b806f80e8bf1c4f4e5a96248393fe6622fc1fc6189d6896d269658f67f914c) -set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/321a652626c63bfea3ea320083a4b14863b80270.tar.gz) -set(TREESITTER_SHA256 8f780289d9524a680e548d891c07dab025241fffdea86f8b55921e4024a84757) - -if(USE_EXISTING_SRC_DIR) - get_cmake_property(VARS VARIABLES) - foreach (VAR ${VARS}) - if(VAR MATCHES "^.*URL$") - unset(${VAR}) +file(READ deps.txt DEPENDENCIES) +STRING(REGEX REPLACE "\n" ";" DEPENDENCIES "${DEPENDENCIES}") +foreach(dep ${DEPENDENCIES}) + STRING(REGEX REPLACE " " ";" dep "${dep}") + list(GET dep 0 name) + list(GET dep 1 value) + + if(name MATCHES "^.*URL$") + mark_as_advanced(${name}) + if(NOT USE_EXISTING_SRC_DIR) + set(${name} ${value} CACHE STRING "") endif() - endforeach() -endif() + elseif(name MATCHES "^.*SHA256$") + set(${name} ${value}) + endif() + +endforeach() if(USE_BUNDLED_UNIBILIUM) include(BuildUnibilium) diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt new file mode 100644 index 0000000000..2ba97fefc0 --- /dev/null +++ b/cmake.deps/deps.txt @@ -0,0 +1,64 @@ +LIBUV_URL https://github.com/libuv/libuv/archive/62c2374a8c005ce9e42088965f8f8af2532c177b.tar.gz +LIBUV_SHA256 c7e89137da65a1cb550ba96b892dfeeabea982bf33b9237bcf9bbcd90f2e70a1 + +MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/c-6.0.0/msgpack-c-6.0.0.tar.gz +MSGPACK_SHA256 3654f5e2c652dc52e0a993e270bb57d5702b262703f03771c152bba51602aeba + +LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/224129a8e64bfa219d35cd03055bf03952f167f6.tar.gz +LUAJIT_SHA256 a9bcd9e646e2b188e1d7e3fb594e04c61dda3b332dfd0378d41be19c1eae9d09 + +LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz +LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333 + +LUAROCKS_URL https://github.com/luarocks/luarocks/archive/v3.9.2.tar.gz +LUAROCKS_SHA256 a0b36cd68586cd79966d0106bb2e5a4f5523327867995fd66bee4237062b3e3b + +UNIBILIUM_URL https://github.com/neovim/unibilium/archive/d72c3598e7ac5d1ebf86ee268b8b4ed95c0fa628.tar.gz +UNIBILIUM_SHA256 9c4747c862ab5e3076dcf8fa8f0ea7a6b50f20ec5905618b9536655596797487 + +LIBTERMKEY_URL https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/libtermkey/0.22-1/libtermkey_0.22.orig.tar.gz +LIBTERMKEY_SHA256 6945bd3c4aaa83da83d80a045c5563da4edd7d0374c62c0d35aec09eb3014600 + +LIBVTERM_URL https://launchpad.net/libvterm/trunk/v0.3/+download/libvterm-0.3.1.tar.gz +LIBVTERM_SHA256 25a8ad9c15485368dfd0a8a9dca1aec8fea5c27da3fa74ec518d5d3787f0c397 + +LUV_URL https://github.com/luvit/luv/archive/093a977b82077591baefe1e880d37dfa2730bd54.tar.gz +LUV_SHA256 222b38b6425f0926218e14e7da81481fdde6f9660c1feac25a53e6fb52e886e6 + +LPEG_URL http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.2.tar.gz +LPEG_SHA256 48d66576051b6c78388faad09b70493093264588fcd0f258ddaab1cdd4a15ffe + +LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.tar.gz +LUA_COMPAT53_SHA256 ad05540d2d96a48725bb79a1def35cf6652a4e2ec26376e2617c8ce2baa6f416 + +CAT_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/cat.exe +CAT_SHA256 93b8d307bb15af3968920bdea3beb869a49d166f9164853c58a4e6ffdcae61c6 +TEE_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/tee.exe +TEE_SHA256 950eea4e17fa3a7e89fa2c55374037b5797b3f1a54fea1304634884ab42ec14d +XXD_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/xxd.exe +XXD_SHA256 7a581e3882d28161cc52850f9a11d634b3eaf2c029276f093c1ed4c90e45a10c + +WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.17/neovim-qt.zip +WINGUI_SHA256 502e386eef677c2c2e0c11d8cbb27f3e12b4d96818369417e8da4129c4580c25 + +WIN32YANK_X86_64_URL https://github.com/equalsraf/win32yank/releases/download/v0.1.1/win32yank-x64.zip +WIN32YANK_X86_64_SHA256 247c9a05b94387a884b49d3db13f806b1677dfc38020f955f719be6902260cd6 + +GETTEXT_URL https://ftp.gnu.org/pub/gnu/gettext/gettext-0.20.1.tar.gz +GETTEXT_SHA256 66415634c6e8c3fa8b71362879ec7575e27da43da562c798a8a2f223e6e47f5c + +LIBICONV_URL https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz +LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 + +TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.2.tar.gz +TREESITTER_C_SHA256 af66fde03feb0df4faf03750102a0d265b007e5d957057b6b293c13116a70af2 +TREESITTER_LUA_URL https://github.com/MunifTanjim/tree-sitter-lua/archive/v0.0.14.tar.gz +TREESITTER_LUA_SHA256 930d0370dc15b66389869355c8e14305b9ba7aafd36edbfdb468c8023395016d +TREESITTER_VIM_URL https://github.com/neovim/tree-sitter-vim/archive/v0.3.0.tar.gz +TREESITTER_VIM_SHA256 403acec3efb7cdb18ff3d68640fc823502a4ffcdfbb71cec3f98aa786c21cbe2 +TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v2.0.0.tar.gz +TREESITTER_VIMDOC_SHA256 1ff8f4afd3a9599dd4c3ce87c155660b078c1229704d1a254433e33794b8f274 +TREESITTER_QUERY_URL https://github.com/nvim-treesitter/tree-sitter-query/archive/v0.1.0.tar.gz +TREESITTER_QUERY_SHA256 e2b806f80e8bf1c4f4e5a96248393fe6622fc1fc6189d6896d269658f67f914c +TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/321a652626c63bfea3ea320083a4b14863b80270.tar.gz +TREESITTER_SHA256 8f780289d9524a680e548d891c07dab025241fffdea86f8b55921e4024a84757 diff --git a/cmake/FindLpeg.cmake b/cmake/FindLpeg.cmake index d4fc6dbd97..4354f815b5 100644 --- a/cmake/FindLpeg.cmake +++ b/cmake/FindLpeg.cmake @@ -10,5 +10,7 @@ ubuntu() find_package_handle_standard_args(Lpeg DEFAULT_MSG LPEG_LIBRARY) mark_as_advanced(LPEG_LIBRARY) -add_library(lpeg INTERFACE) -target_link_libraries(lpeg INTERFACE ${LPEG_LIBRARY}) +# Workaround: use an imported library to prevent cmake from modifying library +# link path. See #23395. +add_library(lpeg UNKNOWN IMPORTED) +set_target_properties(lpeg PROPERTIES IMPORTED_LOCATION ${LPEG_LIBRARY}) diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 01edbe7cbd..e07bfc2209 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -2131,27 +2131,6 @@ start({cmd}, {cmd_args}, {dispatchers}, {extra_spawn_params}) ============================================================================== -Lua module: vim.lsp.sync *lsp-sync* - - *vim.lsp.sync.compute_diff()* -compute_diff({___MissingCloseParenHere___}) - Returns the range table for the difference between prev and curr lines - - Parameters: ~ - • {prev_lines} (table) list of lines - • {curr_lines} (table) list of lines - • {firstline} (integer) line to begin search for first difference - • {lastline} (integer) line to begin search in old_lines for - last difference - • {new_lastline} (integer) line to begin search in new_lines for - last difference - • {offset_encoding} (string) encoding requested by language server - - Return: ~ - (table) TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent - - -============================================================================== Lua module: vim.lsp.protocol *lsp-protocol* *vim.lsp.protocol.make_client_capabilities()* diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index f33cffa22e..bc357ac534 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -49,6 +49,9 @@ iterators |luaref-in|. • Added |vim.treesitter.query.omnifunc()| for treesitter query files (set by default). +• |'smoothscroll'| option to scroll by screen line rather than by text line +when |'wrap'| is set. + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index ba73d79cd3..ab86e56a62 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5652,6 +5652,16 @@ A jump table for the options with a short description can be found at |Q_op|. option. Also see |ins-expandtab|. When 'expandtab' is not set, the number of spaces is minimized by using <Tab>s. + *'smoothscroll'* *'sms'* *'nosmoothscroll'* *'nosms'* +'smoothscroll' 'sms' boolean (default off) + local to window + Scrolling works with screen lines. When 'wrap' is set and the first + line in the window wraps part of it may not be visible, as if it is + above the window. "<<<" is displayed at the start of the first line, + highlighted with |hl-NonText|. + NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y + and scrolling with the mouse. + *'softtabstop'* *'sts'* 'softtabstop' 'sts' number (default 0) local to buffer diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 952f0064e6..c166ecd79d 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -870,6 +870,7 @@ Short explanation of each option: *option-list* 'smartcase' 'scs' no ignore case when pattern has uppercase 'smartindent' 'si' smart autoindenting for C programs 'smarttab' 'sta' use 'shiftwidth' when inserting <Tab> +'smoothscroll' 'sms' scroll by screen lines when 'wrap' is set 'softtabstop' 'sts' number of spaces that <Tab> uses while editing 'spell' enable spell checking 'spellcapcheck' 'spc' pattern to locate end of a sentence diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 4fafc4e2e2..5c799b23f2 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -637,6 +637,7 @@ local extension = { nse = 'lua', rockspec = 'lua', lua = 'lua', + luau = 'luau', lrc = 'lyrics', m = function(path, bufnr) return require('vim.filetype.detect').m(bufnr) diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 03532c33b7..b6b09c58b1 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -1,7 +1,8 @@ local api = vim.api +local bit = require('bit') local handlers = require('vim.lsp.handlers') local util = require('vim.lsp.util') -local bit = require('bit') +local uv = vim.loop --- @class STTokenRange --- @field line integer line number 0-based @@ -94,15 +95,38 @@ end --- ---@private ---@return STTokenRange[] -local function tokens_to_ranges(data, bufnr, client) +local function tokens_to_ranges(data, bufnr, client, request) local legend = client.server_capabilities.semanticTokensProvider.legend local token_types = legend.tokenTypes local token_modifiers = legend.tokenModifiers local ranges = {} + local start = uv.hrtime() + local ms_to_ns = 1000 * 1000 + local yield_interval_ns = 5 * ms_to_ns + local co, is_main = coroutine.running() + local line local start_char = 0 for i = 1, #data, 5 do + -- if this function is called from the main coroutine, let it run to completion with no yield + if not is_main then + local elapsed_ns = uv.hrtime() - start + + if elapsed_ns > yield_interval_ns then + vim.schedule(function() + coroutine.resume(co, util.buf_versions[bufnr]) + end) + if request.version ~= coroutine.yield() then + -- request became stale since the last time the coroutine ran. + -- abandon it by yielding without a way to resume + coroutine.yield() + end + + start = uv.hrtime() + end + end + local delta_line = data[i] line = line and line + delta_line or delta_line local delta_start = data[i + 1] @@ -280,7 +304,7 @@ function STHighlighter:send_request() local c = vim.lsp.get_client_by_id(ctx.client_id) local highlighter = STHighlighter.active[ctx.bufnr] if not err and c and highlighter then - highlighter:process_response(response, c, version) + coroutine.wrap(STHighlighter.process_response)(highlighter, response, c, version) end end, self.bufnr) @@ -315,11 +339,9 @@ function STHighlighter:process_response(response, client, version) return end - -- reset active request - state.active_request = {} - -- skip nil responses if response == nil then + state.active_request = {} return end @@ -347,12 +369,19 @@ function STHighlighter:process_response(response, client, version) tokens = response.data end - -- Update the state with the new results + -- convert token list to highlight ranges + -- this could yield and run over multiple event loop iterations + local highlights = tokens_to_ranges(tokens, self.bufnr, client, state.active_request) + + -- reset active request + state.active_request = {} + + -- update the state with the new results local current_result = state.current_result current_result.version = version current_result.result_id = response.resultId current_result.tokens = tokens - current_result.highlights = tokens_to_ranges(tokens, self.bufnr, client) + current_result.highlights = highlights current_result.namespace_cleared = false -- redraw all windows displaying buffer diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index 7df93d1b2e..f6425d7cb9 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -1,3 +1,5 @@ +local ts = vim.treesitter + local Range = require('vim.treesitter._range') local api = vim.api @@ -32,15 +34,58 @@ function FoldInfo:invalidate_range(srow, erow) end end +--- Efficiently remove items from middle of a list a list. +--- +--- Calling table.remove() in a loop will re-index the tail of the table on +--- every iteration, instead this function will re-index the table exactly +--- once. +--- +--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524 +--- +---@param t any[] +---@param first integer +---@param last integer +local function list_remove(t, first, last) + local n = #t + for i = 0, n - first do + t[first + i] = t[last + 1 + i] + t[last + 1 + i] = nil + end +end + ---@package ---@param srow integer ---@param erow integer function FoldInfo:remove_range(srow, erow) - for i = erow - 1, srow, -1 do - table.remove(self.levels, i + 1) - table.remove(self.levels0, i + 1) - table.remove(self.start_counts, i + 1) - table.remove(self.stop_counts, i + 1) + list_remove(self.levels, srow + 1, erow) + list_remove(self.levels0, srow + 1, erow) + list_remove(self.start_counts, srow + 1, erow) + list_remove(self.stop_counts, srow + 1, erow) +end + +--- Efficiently insert items into the middle of a list. +--- +--- Calling table.insert() in a loop will re-index the tail of the table on +--- every iteration, instead this function will re-index the table exactly +--- once. +--- +--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524 +--- +---@param t any[] +---@param first integer +---@param last integer +---@param v any +local function list_insert(t, first, last, v) + local n = #t + + -- Shift table forward + for i = n - first, 0, -1 do + t[last + 1 + i] = t[first + i] + end + + -- Fill in new values + for i = first, last do + t[i] = v end end @@ -48,12 +93,10 @@ end ---@param srow integer ---@param erow integer function FoldInfo:add_range(srow, erow) - for i = srow, erow - 1 do - table.insert(self.levels, i + 1, '-1') - table.insert(self.levels0, i + 1, -1) - table.insert(self.start_counts, i + 1, nil) - table.insert(self.stop_counts, i + 1, nil) - end + list_insert(self.levels, srow + 1, erow, '-1') + list_insert(self.levels0, srow + 1, erow, -1) + list_insert(self.start_counts, srow + 1, erow, nil) + list_insert(self.stop_counts, srow + 1, erow, nil) end ---@package @@ -90,21 +133,41 @@ local function trim_level(level) return level end +--- If a parser doesn't have any ranges explicitly set, treesitter will +--- return a range with end_row and end_bytes with a value of UINT32_MAX, +--- so clip end_row to the max buffer line. +--- +--- TODO(lewis6991): Handle this generally +--- +--- @param bufnr integer +--- @param erow integer? +--- @return integer +local function normalise_erow(bufnr, erow) + local max_erow = api.nvim_buf_line_count(bufnr) - 1 + return math.min(erow or max_erow, max_erow) +end + ---@param bufnr integer ---@param info TS.FoldInfo ---@param srow integer? ---@param erow integer? local function get_folds_levels(bufnr, info, srow, erow) srow = srow or 0 - erow = erow or api.nvim_buf_line_count(bufnr) + erow = normalise_erow(bufnr, erow) info:invalidate_range(srow, erow) local prev_start = -1 local prev_stop = -1 - vim.treesitter.get_parser(bufnr):for_each_tree(function(tree, ltree) - local query = vim.treesitter.query.get(ltree:lang(), 'folds') + local parser = ts.get_parser(bufnr) + + if not parser:is_valid() then + return + end + + parser:for_each_tree(function(tree, ltree) + local query = ts.query.get(ltree:lang(), 'folds') if not query then return end @@ -112,9 +175,9 @@ local function get_folds_levels(bufnr, info, srow, erow) -- erow in query is end-exclusive local q_erow = erow and erow + 1 or -1 - for id, node, metadata in query:iter_captures(tree:root(), bufnr, srow or 0, q_erow) do + for id, node, metadata in query:iter_captures(tree:root(), bufnr, srow, q_erow) do if query.captures[id] == 'fold' then - local range = vim.treesitter.get_range(node, bufnr, metadata[id]) + local range = ts.get_range(node, bufnr, metadata[id]) local start, _, stop, stop_col = Range.unpack4(range) if stop_col == 0 then @@ -184,13 +247,25 @@ local function recompute_folds() vim._foldupdate() end +--- Schedule a function only if bufnr is loaded +---@param bufnr integer +---@param fn function +local function schedule_if_loaded(bufnr, fn) + vim.schedule(function() + if not api.nvim_buf_is_loaded(bufnr) then + return + end + fn() + end) +end + ---@param bufnr integer ---@param foldinfo TS.FoldInfo ---@param tree_changes Range4[] local function on_changedtree(bufnr, foldinfo, tree_changes) -- For some reason, queries seem to use the old buffer state in on_bytes. -- Get around this by scheduling and manually updating folds. - vim.schedule(function() + schedule_if_loaded(bufnr, function() for _, change in ipairs(tree_changes) do local srow, _, erow = Range.unpack4(change) get_folds_levels(bufnr, foldinfo, srow, erow) @@ -212,7 +287,7 @@ local function on_bytes(bufnr, foldinfo, start_row, old_row, new_row) foldinfo:remove_range(end_row_new, end_row_old) elseif new_row > old_row then foldinfo:add_range(start_row, end_row_new) - vim.schedule(function() + schedule_if_loaded(bufnr, function() get_folds_levels(bufnr, foldinfo, start_row, end_row_new) recompute_folds() end) @@ -226,7 +301,7 @@ function M.foldexpr(lnum) lnum = lnum or vim.v.lnum local bufnr = api.nvim_get_current_buf() - if not vim.treesitter._has_parser(bufnr) or not lnum then + if not ts._has_parser(bufnr) or not lnum then return '0' end @@ -234,7 +309,7 @@ function M.foldexpr(lnum) foldinfos[bufnr] = FoldInfo.new() get_folds_levels(bufnr, foldinfos[bufnr]) - local parser = vim.treesitter.get_parser(bufnr) + local parser = ts.get_parser(bufnr) parser:register_cbs({ on_changedtree = function(tree_changes) on_changedtree(bufnr, foldinfos[bufnr], tree_changes) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 19cea32367..1adf6759fa 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -262,7 +262,7 @@ function LanguageTree:parse() tcall(self._parser.parse, self._parser, self._trees[i], self._source, true) -- Pass ranges if this is an initial parse - local cb_changes = self._trees[i] and tree_changes or ranges + local cb_changes = self._trees[i] and tree_changes or tree:included_ranges(true) self:_do_callback('changedtree', cb_changes, tree) self._trees[i] = tree diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 0d10ac4758..b7b9c61123 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -305,6 +305,9 @@ call <SID>Header(gettext("displaying text")) call <SID>AddOption("scroll", gettext("number of lines to scroll for CTRL-U and CTRL-D")) call append("$", "\t" .. s:local_to_window) call <SID>OptionL("scr") +call <SID>AddOption("smoothscroll", gettext("scroll by screen line")) +call append("$", "\t" .. s:local_to_window) +call <SID>BinOptionL("sms") call <SID>AddOption("scrolloff", gettext("number of screen lines to show around the cursor")) call append("$", " \tset so=" . &so) call <SID>AddOption("wrap", gettext("long lines wrap")) diff --git a/scripts/bump_deps.lua b/scripts/bump_deps.lua index f980e800cf..6a049d136a 100755 --- a/scripts/bump_deps.lua +++ b/scripts/bump_deps.lua @@ -63,6 +63,7 @@ local function rm_file_if_present(path_to_file) end local nvim_src_dir = vim.fn.getcwd() +local deps_file = nvim_src_dir .. '/' .. 'cmake.deps/deps.txt' local temp_dir = nvim_src_dir .. '/tmp' run({ 'mkdir', '-p', temp_dir }) @@ -127,26 +128,24 @@ end local function write_cmakelists_line(symbol, kind, value) require_executable('sed') - local cmakelists_path = nvim_src_dir .. '/' .. 'cmake.deps/CMakeLists.txt' run_die({ 'sed', '-i', '-e', - 's/set(' + 's/' .. symbol .. '_' .. kind .. '.*$' - .. '/set(' + .. '/' .. symbol .. '_' .. kind .. ' ' .. value - .. ')' .. '/', - cmakelists_path, - }, 'Failed to write ' .. cmakelists_path) + deps_file, + }, 'Failed to write ' .. deps_file) end local function explicit_create_branch(dep) @@ -181,8 +180,6 @@ local function update_cmakelists(dependency, archive, comment) verify_branch(dependency.name) - local changed_file = nvim_src_dir .. '/' .. 'cmake.deps/CMakeLists.txt' - p('Updating ' .. dependency.name .. ' to ' .. archive.url .. '\n') write_cmakelists_line(dependency.symbol, 'URL', archive.url:gsub('/', '\\/')) write_cmakelists_line(dependency.symbol, 'SHA256', archive.sha) @@ -190,7 +187,7 @@ local function update_cmakelists(dependency, archive, comment) { 'git', 'commit', - changed_file, + deps_file, '-m', commit_prefix .. 'bump ' .. dependency.name .. ' to ' .. comment, }, @@ -201,10 +198,9 @@ end local function verify_cmakelists_committed() require_executable('git') - local cmakelists_path = nvim_src_dir .. '/' .. 'cmake.deps/CMakeLists.txt' run_die( - { 'git', 'diff', '--quiet', 'HEAD', '--', cmakelists_path }, - cmakelists_path .. ' has uncommitted changes' + { 'git', 'diff', '--quiet', 'HEAD', '--', deps_file }, + deps_file .. ' has uncommitted changes' ) end diff --git a/scripts/gen_help_html.lua b/scripts/gen_help_html.lua index e2ab70eca2..1bddd3aa8b 100644 --- a/scripts/gen_help_html.lua +++ b/scripts/gen_help_html.lua @@ -927,6 +927,9 @@ local function gen_css(fname) /* Tag pseudo-header common in :help docs. */ .help-tag-right { color: var(--tag-color); + margin-left: auto; + margin-right: 0; + float: right; } h1 .help-tag, h2 .help-tag, h3 .help-tag { font-size: smaller; diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index d0686e92bc..e9209e219e 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -222,7 +222,6 @@ CONFIG = { 'util.lua', 'log.lua', 'rpc.lua', - 'sync.lua', 'protocol.lua', ], 'files': [ diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 02226f3cc4..ce8ee21882 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -192,6 +192,8 @@ typedef struct { #define w_p_rlc w_onebuf_opt.wo_rlc // 'rightleftcmd' long wo_scr; #define w_p_scr w_onebuf_opt.wo_scr // 'scroll' + int wo_sms; +#define w_p_sms w_onebuf_opt.wo_sms // 'smoothscroll' int wo_spell; #define w_p_spell w_onebuf_opt.wo_spell // 'spell' int wo_cuc; @@ -1163,11 +1165,12 @@ struct window_S { bool w_botfill; // true when filler lines are actually // below w_topline (at end of file) bool w_old_botfill; // w_botfill at last redraw - colnr_T w_leftcol; // window column number of the left most + colnr_T w_leftcol; // screen column number of the left most // character in the window; used when // 'wrap' is off - colnr_T w_skipcol; // starting column when a single line - // doesn't fit in the window + colnr_T w_skipcol; // starting screen column for the first + // line in the window; used when 'wrap' is + // on; does not include win_col_off() // six fields that are only used when there is a WinScrolled autocommand linenr_T w_last_topline; ///< last known value for w_topline @@ -1220,6 +1223,7 @@ struct window_S { int w_valid; pos_T w_valid_cursor; // last known position of w_cursor, used to adjust w_valid colnr_T w_valid_leftcol; // last known w_leftcol + colnr_T w_valid_skipcol; // last known w_skipcol bool w_viewport_invalid; linenr_T w_viewport_last_topline; // topline when the viewport was last updated diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 6d90b32545..8ba0b2ffb3 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -115,7 +115,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a col = wcol; if ((addspaces || finetune) && !VIsual_active) { - curwin->w_curswant = linetabsize(line) + one_more; + curwin->w_curswant = linetabsize_str(line) + one_more; if (curwin->w_curswant > 0) { curwin->w_curswant--; } @@ -129,7 +129,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a && curwin->w_width_inner != 0 && wcol >= (colnr_T)width && width > 0) { - csize = linetabsize(line); + csize = linetabsize_str(line); if (csize > 0) { csize--; } @@ -439,23 +439,27 @@ void adjust_cursor_col(void) } } -/// When curwin->w_leftcol has changed, adjust the cursor position. +/// Set "curwin->w_leftcol" to "leftcol". +/// Adjust the cursor position if needed. /// /// @return true if the cursor was moved. -bool leftcol_changed(void) +bool set_leftcol(colnr_T leftcol) { - // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long. - // Perhaps we can change p_siso to int. - int64_t lastcol; - colnr_T s, e; - bool retval = false; + // Return quickly when there is no change. + if (curwin->w_leftcol == leftcol) { + return false; + } + curwin->w_leftcol = leftcol; changed_cline_bef_curs(); - lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1; + // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long. + // Perhaps we can change p_siso to int. + int64_t lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1; validate_virtcol(); + bool retval = false; // If the cursor is right or left of the screen, move it to last or first - // character. + // visible character. long siso = get_sidescrolloff_value(curwin); if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) { retval = true; @@ -468,6 +472,7 @@ bool leftcol_changed(void) // If the start of the character under the cursor is not on the screen, // advance the cursor one more char. If this fails (last char of the // line) adjust the scrolling. + colnr_T s, e; getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e); if (e > (colnr_T)lastcol) { retval = true; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 9bdf6a8255..e6cdf3d60d 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -168,7 +168,7 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col) return; } - width1 = wp->w_width - cur_col_off; + width1 = wp->w_width_inner - cur_col_off; width2 = width1 + win_col_off2(wp); *left_col = 0; @@ -595,19 +595,23 @@ static int get_line_number_attr(win_T *wp, winlinevars_T *wlv) static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int sign_idx, int sign_num_attr, int sign_cul_attr) { + bool has_cpo_n = vim_strchr(p_cpo, CPO_NUMCOL) != NULL; + if ((wp->w_p_nu || wp->w_p_rnu) - && (wlv->row == wlv->startrow + wlv->filler_lines - || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) { - // If 'signcolumn' is set to 'number' and a sign is present - // in "lnum", then display the sign instead of the line - // number. + && (wlv->row == wlv->startrow + wlv->filler_lines || !has_cpo_n) + // there is no line number in a wrapped line when "n" is in + // 'cpoptions', but 'breakindent' assumes it anyway. + && !((has_cpo_n && !wp->w_p_bri) && wp->w_skipcol > 0 && wlv->lnum == wp->w_topline)) { + // If 'signcolumn' is set to 'number' and a sign is present in "lnum", + // then display the sign instead of the line number. if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) { get_sign_display_info(true, wp, wlv, sign_idx, sign_cul_attr); } else { // Draw the line number (empty space after wrapping). - if (wlv->row == wlv->startrow + wlv->filler_lines) { + if (wlv->row == wlv->startrow + wlv->filler_lines + && (wp->w_skipcol == 0 || wlv->row > 0 || (wp->w_p_nu && wp->w_p_rnu))) { get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra)); - if (wp->w_skipcol > 0) { + if (wp->w_skipcol > 0 && wlv->startrow == 0) { for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' '; wlv->p_extra++) { *wlv->p_extra = '-'; } @@ -754,7 +758,7 @@ static void handle_breakindent(win_T *wp, winlinevars_T *wlv) wlv->n_extra = 0; } } - if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) { + if (wp->w_skipcol > 0 && wlv->startrow == 0 && wp->w_p_wrap && wp->w_briopt_sbr) { wlv->need_showbreak = false; } // Correct end of highlighted area for 'breakindent', @@ -804,7 +808,7 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv) wlv->c_final = NUL; wlv->n_extra = (int)strlen(sbr); wlv->char_attr = win_hl_attr(wp, HLF_AT); - if (wp->w_skipcol == 0 || !wp->w_p_wrap) { + if (wp->w_skipcol == 0 || wlv->startrow != 0 || !wp->w_p_wrap) { wlv->need_showbreak = false; } wlv->vcol_sbr = wlv->vcol + mb_charlen(sbr); @@ -1379,7 +1383,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the // first character to be displayed. if (wp->w_p_wrap) { - v = wp->w_skipcol; + v = startrow == 0 ? wp->w_skipcol : 0; } else { v = wp->w_leftcol; } @@ -2595,7 +2599,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (c == NUL) { // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line. if (wp->w_p_wrap) { - v = wp->w_skipcol; + v = wlv.startrow == 0 ? wp->w_skipcol : 0; } else { v = wp->w_leftcol; } diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index b5e516005b..ec5163f37a 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1447,6 +1447,26 @@ static void win_update(win_T *wp, DecorProviders *providers) init_search_hl(wp, &screen_search_hl); + // Make sure skipcol is valid, it depends on various options and the window + // width. + if (wp->w_skipcol > 0) { + int w = 0; + int width1 = wp->w_width_inner - win_col_off(wp); + int width2 = width1 + win_col_off2(wp); + int add = width1; + + while (w < wp->w_skipcol) { + if (w > 0) { + add = width2; + } + w += add; + } + if (w != wp->w_skipcol) { + // always round down, the higher value may not be valid + wp->w_skipcol = w - add; + } + } + // Force redraw when width of 'number' or 'relativenumber' column // changes. int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 0fb1102f4f..2078fc4251 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -193,7 +193,7 @@ static void insert_enter(InsertState *s) } } - Insstart_textlen = (colnr_T)linetabsize(get_cursor_line_ptr()); + Insstart_textlen = linetabsize_str(get_cursor_line_ptr()); Insstart_blank_vcol = MAXCOL; if (!did_ai) { @@ -2251,7 +2251,7 @@ int stop_arrow(void) // right, except when nothing was inserted yet. update_Insstart_orig = false; } - Insstart_textlen = (colnr_T)linetabsize(get_cursor_line_ptr()); + Insstart_textlen = linetabsize_str(get_cursor_line_ptr()); if (u_save_cursor() == OK) { arrow_used = false; @@ -2449,6 +2449,7 @@ void beginline(int flags) } curwin->w_set_curswant = true; } + adjust_skipcol(); } // oneright oneleft cursor_down cursor_up @@ -2490,6 +2491,7 @@ int oneright(void) curwin->w_cursor.col += l; curwin->w_set_curswant = true; + adjust_skipcol(); return OK; } @@ -2538,6 +2540,7 @@ int oneleft(void) // if the character on the left of the current cursor is a multi-byte // character, move to its first byte mb_adjust_cursor(); + adjust_skipcol(); return OK; } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 1f5a6eaec4..bfda2c4b9b 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2587,19 +2587,13 @@ void ex_function(exarg_T *eap) // Check for ":let v =<< [trim] EOF" // and ":let [a, b] =<< [trim] EOF" - arg = skipwhite(skiptowhite(p)); - if (*arg == '[') { - arg = vim_strchr(arg, ']'); - } - if (arg != NULL) { - arg = skipwhite(skiptowhite(arg)); - if (arg[0] == '=' - && arg[1] == '<' - && arg[2] == '<' - && (p[0] == 'l' - && p[1] == 'e' - && (!ASCII_ISALNUM(p[2]) - || (p[2] == 't' && !ASCII_ISALNUM(p[3]))))) { + arg = p; + if (checkforcmd(&arg, "let", 2)) { + while (vim_strchr("$@&", *arg) != NULL) { + arg++; + } + arg = skipwhite(find_name_end(arg, NULL, NULL, FNE_INCL_BR)); + if (arg[0] == '=' && arg[1] == '<' && arg[2] == '<') { p = skipwhite(arg + 3); while (true) { if (strncmp(p, "trim", 4) == 0) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index f276e8ae24..2c31f742c3 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -346,10 +346,8 @@ static int linelen(int *has_tab) last > first && ascii_iswhite(last[-1]); last--) {} char save = *last; *last = NUL; - // Get line length. - len = linetabsize(line); - // Check for embedded TAB. - if (has_tab != NULL) { + len = linetabsize_str(line); // Get line length. + if (has_tab != NULL) { // Check for embedded TAB. *has_tab = vim_strchr(first, TAB) != NULL; } *last = save; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 5018c9268b..af2ec3356f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -504,6 +504,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat } validate_cursor(); + // May redraw the status line to show the cursor position. if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { curwin->w_redr_status = true; @@ -598,6 +599,7 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool magic_overruled = s->magic_overruled_save; validate_cursor(); // needed for TAB + status_redraw_all(); redraw_all_later(UPD_SOME_VALID); if (call_update_screen) { update_screen(); diff --git a/src/nvim/generators/gen_vimvim.lua b/src/nvim/generators/gen_vimvim.lua index 09a7cab0c6..29355d3cda 100644 --- a/src/nvim/generators/gen_vimvim.lua +++ b/src/nvim/generators/gen_vimvim.lua @@ -112,13 +112,12 @@ for _, au in ipairs(auevents.events) do w(' ' .. au) end end -for au, _ in pairs(auevents.aliases) do - if not auevents.nvim_specific[au] then - if lld.line_length > 850 then - w('\n' .. vimau_start) - end - w(' ' .. au) +for _, au in pairs(auevents.aliases) do + if lld.line_length > 850 then + w('\n' .. vimau_start) end + -- au[1] is aliased to au[2] + w(' ' .. au[1]) end local nvimau_start = 'syn keyword nvimAutoEvent contained ' diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 7745daf69a..037606c38f 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -22,7 +22,7 @@ #include "nvim/highlight.h" #include "nvim/log.h" #include "nvim/message.h" -#include "nvim/option_defs.h" +#include "nvim/option.h" #include "nvim/types.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -503,6 +503,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle int col = 0; bool redraw_next; // redraw_this for next character bool clear_next = false; + bool topline = row == 0; int char_cells; // 1: normal char // 2: occupies two display cells int start_dirty = -1, end_dirty = 0; @@ -529,6 +530,30 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle max_off_from = linebuf_size; max_off_to = grid->line_offset[row] + (size_t)grid->cols; + // Take care of putting "<<<" on the first line for 'smoothscroll'. + if (topline && wp->w_skipcol > 0 + // do not overwrite the 'showbreak' text with "<<<" + && *get_showbreak_value(wp) == NUL + // do not overwrite the 'listchars' "precedes" text with "<<<" + && !(wp->w_p_list && wp->w_p_lcs_chars.prec != 0)) { + int off = 0; + int skip = 0; + if (wp->w_p_nu && wp->w_p_rnu) { + // do not overwrite the line number, change "123 text" to + // "123>>>xt". + while (skip < wp->w_width_inner && ascii_isdigit(*linebuf_char[off])) { + off++; + skip++; + } + } + + for (int i = 0; i < 3 && i + skip < wp->w_width_inner; i++) { + schar_from_ascii(linebuf_char[off], '<'); + linebuf_attr[off] = HL_ATTR(HLF_AT); + off++; + } + } + if (rlflag) { // Clear rest first, because it's left of the text. if (clear_width > 0) { diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index e03eec184f..8189fde83c 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -223,7 +223,7 @@ static int get_fpos_of_mouse(pos_T *mpos) } // winpos and height may change in win_enter()! - if (winrow + wp->w_winbar_height >= wp->w_height) { // In (or below) status line + if (winrow >= wp->w_height_inner) { // In (or below) status line return IN_STATUS_LINE; } @@ -231,7 +231,7 @@ static int get_fpos_of_mouse(pos_T *mpos) return MOUSE_WINBAR; } - if (wincol >= wp->w_width) { // In vertical separator line + if (wincol >= wp->w_width_inner) { // In vertical separator line return IN_SEP_LINE; } @@ -557,10 +557,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) if (VIsual_active) { // set MOUSE_MAY_STOP_VIS if we are outside the selection // or the current window (might have false negative here) - if (mouse_row < curwin->w_winrow - || mouse_row > (curwin->w_winrow + curwin->w_height)) { - jump_flags = MOUSE_MAY_STOP_VIS; - } else if (m_pos_flag != IN_BUFFER) { + if (m_pos_flag != IN_BUFFER) { jump_flags = MOUSE_MAY_STOP_VIS; } else { if (VIsual_mode == 'V') { @@ -1413,9 +1410,22 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) } else { row -= win_get_fill(win, lnum); } - count = plines_win_nofill(win, lnum, true); + count = plines_win_nofill(win, lnum, false); } else { - count = plines_win(win, lnum, true); + count = plines_win(win, lnum, false); + } + + if (win->w_skipcol > 0 && lnum == win->w_topline) { + // Adjust for 'smoothscroll' clipping the top screen lines. + // A similar formula is used in curs_columns(). + int width1 = win->w_width_inner - win_col_off(win); + int skip_lines = 0; + if (win->w_skipcol > width1) { + skip_lines = (win->w_skipcol - width1) / (width1 + win_col_off2(win)) + 1; + } else if (win->w_skipcol > 0) { + skip_lines = 1; + } + count -= skip_lines; } if (count > row) { @@ -1439,8 +1449,11 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) col = off; } col += row * (win->w_width_inner - off); - // add skip column (for long wrapping line) - col += win->w_skipcol; + + // Add skip column for the topline. + if (lnum == win->w_topline) { + col += win->w_skipcol; + } } if (!win->w_p_wrap) { @@ -1651,8 +1664,6 @@ bool mouse_scroll_horiz(int dir) return false; } - curwin->w_leftcol = (colnr_T)leftcol; - // When the line of the cursor is too short, move the cursor to the // longest visible line. if (!virtual_active() @@ -1661,7 +1672,7 @@ bool mouse_scroll_horiz(int dir) curwin->w_cursor.col = 0; } - return leftcol_changed(); + return set_leftcol(leftcol); } /// Adjusts the clicked column position when 'conceallevel' > 0 diff --git a/src/nvim/move.c b/src/nvim/move.c index b749d07d15..447926ceb8 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -57,6 +57,43 @@ typedef struct { # include "move.c.generated.h" #endif +/// Reduce "n" for the screen lines skipped with "wp->w_skipcol". +static int adjust_plines_for_skipcol(win_T *wp, int n) +{ + if (wp->w_skipcol == 0) { + return n; + } + + int off = 0; + int width = wp->w_width_inner - win_col_off(wp); + if (wp->w_skipcol >= width) { + off++; + int skip = wp->w_skipcol - width; + width -= win_col_off2(wp); + while (skip >= width) { + off++; + skip -= width; + } + } + wp->w_valid &= ~VALID_WROW; + return n - off; +} + +/// Return how many lines "lnum" will take on the screen, taking into account +/// whether it is the first line, whether w_skipcol is non-zero and limiting to +/// the window height. +static int plines_correct_topline(win_T *wp, linenr_T lnum, linenr_T *nextp, bool *foldedp) +{ + int n = plines_win_full(wp, lnum, nextp, foldedp, true, false); + if (lnum == wp->w_topline) { + n = adjust_plines_for_skipcol(wp, n); + } + if (n > wp->w_height_inner) { + return wp->w_height_inner; + } + return n; +} + // Compute wp->w_botline for the current wp->w_topline. Can be called after // wp->w_topline changed. static void comp_botline(win_T *wp) @@ -78,7 +115,7 @@ static void comp_botline(win_T *wp) for (; lnum <= wp->w_buffer->b_ml.ml_line_count; lnum++) { linenr_T last = lnum; bool folded; - int n = plines_win_full(wp, lnum, &last, &folded, true); + int n = plines_correct_topline(wp, lnum, &last, &folded); if (lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum) { wp->w_cline_row = done; wp->w_cline_height = n; @@ -127,6 +164,51 @@ static void redraw_for_cursorcolumn(win_T *wp) } } +/// Calculates how much overlap the smoothscroll marker "<<<" overlaps with +/// buffer text for curwin. +/// Parameter "extra2" should be the padding on the 2nd line, not the first +/// line. +/// Returns the number of columns of overlap with buffer text, excluding the +/// extra padding on the ledge. +static int smoothscroll_marker_overlap(win_T *wp, int extra2) +{ + // We don't draw the <<< marker when in showbreak mode, thus no need to + // account for it. See grid_put_linebuf(). + if (*get_showbreak_value(wp) != NUL) { + return 0; + } + return extra2 > 3 ? 0 : 3 - extra2; +} + +/// Calculates the skipcol offset for window "wp" given how many +/// physical lines we want to scroll down. +static int skipcol_from_plines(win_T *wp, int plines_off) +{ + int width1 = wp->w_width_inner - win_col_off(wp); + + int skipcol = 0; + if (plines_off > 0) { + skipcol += width1; + } + if (plines_off > 1) { + skipcol += (width1 + win_col_off2(wp)) * (plines_off - 1); + } + return skipcol; +} + +/// Set wp->s_skipcol to zero and redraw later if needed. +static void reset_skipcol(win_T *wp) +{ + if (wp->w_skipcol != 0) { + wp->w_skipcol = 0; + + // Should use the least expensive way that displays all that changed. + // UPD_NOT_VALID is too expensive, UPD_REDRAW_TOP does not redraw + // enough when the top line gets another screen line. + redraw_later(wp, UPD_SOME_VALID); + } +} + // Update curwin->w_topline to move the cursor onto the screen. void update_topline(win_T *wp) { @@ -178,7 +260,7 @@ void update_topline(win_T *wp) bool check_topline = false; // If the cursor is above or near the top of the window, scroll the window // to show the line the cursor is in, with 'scrolloff' context. - if (wp->w_topline > 1) { + if (wp->w_topline > 1 || wp->w_skipcol > 0) { // If the cursor is above topline, scrolling is always needed. // If the cursor is far below topline and there is no folding, // scrolling down is never needed. @@ -186,6 +268,17 @@ void update_topline(win_T *wp) check_topline = true; } else if (check_top_offset()) { check_topline = true; + } else if (wp->w_skipcol > 0 && wp->w_cursor.lnum == wp->w_topline) { + colnr_T vcol; + + // Check that the cursor position is visible. Add columns for the + // smoothscroll marker "<<<" displayed in the top-left if needed. + getvvcol(wp, &wp->w_cursor, &vcol, NULL, NULL); + int smoothscroll_overlap = smoothscroll_marker_overlap(wp, + win_col_off(wp) - win_col_off2(wp)); + if (wp->w_skipcol + smoothscroll_overlap > vcol) { + check_topline = true; + } } } // Check if there are more filler lines than allowed. @@ -314,12 +407,9 @@ void update_topline(win_T *wp) if (wp->w_topline != old_topline || wp->w_topfill != old_topfill) { dollar_vcol = -1; - if (wp->w_skipcol != 0) { - wp->w_skipcol = 0; - redraw_later(wp, UPD_NOT_VALID); - } else { - redraw_later(wp, UPD_VALID); - } + redraw_later(wp, UPD_VALID); + reset_skipcol(wp); + // May need to set w_skipcol when cursor in w_topline. if (wp->w_cursor.lnum == wp->w_topline) { validate_cursor(); @@ -392,7 +482,15 @@ void check_cursor_moved(win_T *wp) |VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE); wp->w_valid_cursor = wp->w_cursor; wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; wp->w_viewport_invalid = true; + } else if (wp->w_skipcol != wp->w_valid_skipcol) { + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL + |VALID_CHEIGHT|VALID_CROW + |VALID_BOTLINE|VALID_BOTLINE_AP); + wp->w_valid_cursor = wp->w_cursor; + wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; } else if (wp->w_cursor.col != wp->w_valid_cursor.col || wp->w_leftcol != wp->w_valid_leftcol || wp->w_cursor.coladd != @@ -564,7 +662,7 @@ static void curs_rows(win_T *wp) } else { linenr_T last = lnum; bool folded; - int n = plines_win_full(wp, lnum, &last, &folded, false); + int n = plines_correct_topline(wp, lnum, &last, &folded); lnum = last + 1; if (folded && lnum > wp->w_cursor.lnum) { break; @@ -581,7 +679,7 @@ static void curs_rows(win_T *wp) && (!wp->w_lines[i].wl_valid || wp->w_lines[i].wl_lnum != wp->w_cursor.lnum))) { wp->w_cline_height = plines_win_full(wp, wp->w_cursor.lnum, NULL, - &wp->w_cline_folded, true); + &wp->w_cline_folded, true, true); } else if (i > wp->w_lines_valid) { // a line that is too long to fit on the last screen line wp->w_cline_height = 0; @@ -628,7 +726,7 @@ void validate_cheight(void) curwin->w_cline_height = plines_win_full(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cline_folded, - true); + true, true); curwin->w_valid |= VALID_CHEIGHT; } @@ -679,8 +777,8 @@ int curwin_col_off(void) } // Return the difference in column offset for the second screen line of a -// wrapped line. It's 8 if 'number' or 'relativenumber' is on and 'n' is in -// 'cpoptions'. +// wrapped line. It's positive if 'number' or 'relativenumber' is on and 'n' +// is in 'cpoptions'. int win_col_off2(win_T *wp) { if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) != NULL) { @@ -694,19 +792,14 @@ int curwin_col_off2(void) return win_col_off2(curwin); } -// Compute curwin->w_wcol and curwin->w_virtcol. -// Also updates curwin->w_wrow and curwin->w_cline_row. -// Also updates curwin->w_leftcol. +// Compute wp->w_wcol and wp->w_virtcol. +// Also updates wp->w_wrow and wp->w_cline_row. +// Also updates wp->w_leftcol. // @param may_scroll when true, may scroll horizontally void curs_columns(win_T *wp, int may_scroll) { - int n; - int width = 0; colnr_T startcol; colnr_T endcol; - colnr_T prev_skipcol; - long so = get_scrolloff_value(wp); - long siso = get_sidescrolloff_value(wp); // First make sure that w_topline is valid (after moving the cursor). update_topline(wp); @@ -736,8 +829,11 @@ void curs_columns(win_T *wp, int may_scroll) // Now compute w_wrow, counting screen lines from w_cline_row. wp->w_wrow = wp->w_cline_row; - int textwidth = wp->w_width_inner - extra; - if (textwidth <= 0) { + int n; + int width1 = wp->w_width_inner - extra; // text width for first screen line + int width2 = 0; // text width for second and later screen line + bool did_sub_skipcol = false; + if (width1 <= 0) { // No room for text, put cursor in last char of window. // If not wrapping, the last non-empty line. wp->w_wcol = wp->w_width_inner - 1; @@ -747,13 +843,28 @@ void curs_columns(win_T *wp, int may_scroll) wp->w_wrow = wp->w_height_inner - 1 - wp->w_empty_rows; } } else if (wp->w_p_wrap && wp->w_width_inner != 0) { - width = textwidth + win_col_off2(wp); + width2 = width1 + win_col_off2(wp); + + // skip columns that are not visible + if (wp->w_cursor.lnum == wp->w_topline + && wp->w_skipcol > 0 + && wp->w_wcol >= wp->w_skipcol) { + // Deduct by multiples of width2. This allows the long line wrapping + // formula below to correctly calculate the w_wcol value when wrapping. + if (wp->w_skipcol <= width1) { + wp->w_wcol -= width2; + } else { + wp->w_wcol -= width2 * (((wp->w_skipcol - width1) / width2) + 1); + } + + did_sub_skipcol = true; + } // long line wrapping, adjust wp->w_wrow if (wp->w_wcol >= wp->w_width_inner) { // this same formula is used in validate_cursor_col() - n = (wp->w_wcol - wp->w_width_inner) / width + 1; - wp->w_wcol -= n * width; + n = (wp->w_wcol - wp->w_width_inner) / width2 + 1; + wp->w_wcol -= n * width2; wp->w_wrow += n; // When cursor wraps to first char of next line in Insert @@ -775,6 +886,7 @@ void curs_columns(win_T *wp, int may_scroll) // If Cursor is right of the screen, scroll leftwards // If we get closer to the edge than 'sidescrolloff', scroll a little // extra + long siso = get_sidescrolloff_value(wp); assert(siso <= INT_MAX); int off_left = startcol - wp->w_leftcol - (int)siso; int off_right = @@ -785,8 +897,8 @@ void curs_columns(win_T *wp, int may_scroll) // When far off or not enough room on either side, put cursor in // middle of window. int new_leftcol; - if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left) { - new_leftcol = wp->w_wcol - extra - textwidth / 2; + if (p_ss == 0 || diff >= width1 / 2 || off_right >= off_left) { + new_leftcol = curwin->w_wcol - extra - width1 / 2; } else { if (diff < p_ss) { assert(p_ss <= INT_MAX); @@ -823,9 +935,9 @@ void curs_columns(win_T *wp, int may_scroll) wp->w_wrow += win_get_fill(wp, wp->w_cursor.lnum); } - prev_skipcol = wp->w_skipcol; - int plines = 0; + long so = get_scrolloff_value(wp); + colnr_T prev_skipcol = wp->w_skipcol; if ((wp->w_wrow >= wp->w_height_inner || ((prev_skipcol > 0 || wp->w_wrow + so >= wp->w_height_inner) @@ -833,7 +945,7 @@ void curs_columns(win_T *wp, int may_scroll) >= wp->w_height_inner)) && wp->w_height_inner != 0 && wp->w_cursor.lnum == wp->w_topline - && width > 0 + && width2 > 0 && wp->w_width_inner != 0) { // Cursor past end of screen. Happens with a single line that does // not fit on screen. Find a skipcol to show the text around the @@ -842,7 +954,7 @@ void curs_columns(win_T *wp, int may_scroll) // 2: Less than "p_so" lines below // 3: both of them extra = 0; - if (wp->w_skipcol + so * width > wp->w_virtcol) { + if (wp->w_skipcol + so * width2 > wp->w_virtcol) { extra = 1; } // Compute last display line of the buffer line that we want at the @@ -857,13 +969,13 @@ void curs_columns(win_T *wp, int may_scroll) } else { n = plines; } - if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width - so) { + if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width2 - so) { extra += 2; } - if (extra == 3 || plines <= so * 2) { + if (extra == 3 || wp->w_height_inner <= so * 2) { // not enough room for 'scrolloff', put cursor in the middle - n = wp->w_virtcol / width; + n = wp->w_virtcol / width2; if (n > wp->w_height_inner / 2) { n -= wp->w_height_inner / 2; } else { @@ -873,51 +985,58 @@ void curs_columns(win_T *wp, int may_scroll) if (n > plines - wp->w_height_inner + 1) { n = plines - wp->w_height_inner + 1; } - wp->w_skipcol = n * width; + wp->w_skipcol = n * width2; } else if (extra == 1) { // less than 'scrolloff' lines above, decrease skipcol assert(so <= INT_MAX); - extra = (wp->w_skipcol + (int)so * width - wp->w_virtcol - + width - 1) / width; + extra = (wp->w_skipcol + (int)so * width2 - wp->w_virtcol + width2 - 1) / width2; if (extra > 0) { - if ((colnr_T)(extra * width) > wp->w_skipcol) { - extra = wp->w_skipcol / width; + if ((colnr_T)(extra * width2) > wp->w_skipcol) { + extra = wp->w_skipcol / width2; } - wp->w_skipcol -= extra * width; + wp->w_skipcol -= extra * width2; } } else if (extra == 2) { // less than 'scrolloff' lines below, increase skipcol - endcol = (n - wp->w_height_inner + 1) * width; + endcol = (n - wp->w_height_inner + 1) * width2; while (endcol > wp->w_virtcol) { - endcol -= width; + endcol -= width2; } if (endcol > wp->w_skipcol) { wp->w_skipcol = endcol; } } - wp->w_wrow -= wp->w_skipcol / width; + // adjust w_wrow for the changed w_skipcol + if (did_sub_skipcol) { + wp->w_wrow -= (wp->w_skipcol - prev_skipcol) / width2; + } else { + wp->w_wrow -= wp->w_skipcol / width2; + } + if (wp->w_wrow >= wp->w_height_inner) { // small window, make sure cursor is in it extra = wp->w_wrow - wp->w_height_inner + 1; - wp->w_skipcol += extra * width; + wp->w_skipcol += extra * width2; wp->w_wrow -= extra; } // extra could be either positive or negative - extra = ((int)prev_skipcol - (int)wp->w_skipcol) / width; + extra = (prev_skipcol - wp->w_skipcol) / width2; win_scroll_lines(wp, 0, extra); - } else { + } else if (!wp->w_p_sms) { wp->w_skipcol = 0; } if (prev_skipcol != wp->w_skipcol) { - redraw_later(wp, UPD_NOT_VALID); + redraw_later(wp, UPD_SOME_VALID); } - redraw_for_cursorcolumn(curwin); + redraw_for_cursorcolumn(wp); - // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise + // now w_leftcol and w_skipcol are valid, avoid check_cursor_moved() + // thinking otherwise wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } @@ -966,18 +1085,18 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, // similar to what is done in validate_cursor_col() colnr_T col = scol; col += off; - int width = wp->w_width - off + win_col_off2(wp); + int width = wp->w_width_inner - off + win_col_off2(wp); // long line wrapping, adjust row - if (wp->w_p_wrap && col >= (colnr_T)wp->w_width && width > 0) { + if (wp->w_p_wrap && col >= (colnr_T)wp->w_width_inner && width > 0) { // use same formula as what is used in curs_columns() - rowoff = visible_row ? ((col - wp->w_width) / width + 1) : 0; + rowoff = visible_row ? ((col - wp->w_width_inner) / width + 1) : 0; col -= rowoff * width; } col -= wp->w_leftcol; - if (col >= 0 && col < wp->w_width && row + rowoff <= wp->w_height) { + if (col >= 0 && col < wp->w_width_inner && row + rowoff <= wp->w_height_inner) { coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_wincol_off) + 1; row += local ? 0 : wp->w_winrow + wp->w_winrow_off; } else { @@ -1064,32 +1183,69 @@ void f_virtcol2col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool scrolldown(long line_count, int byfold) { int done = 0; // total # of physical lines done + int width1 = 0; + int width2 = 0; + bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + + if (do_sms) { + width1 = curwin->w_width_inner - curwin_col_off(); + width2 = width1 + curwin_col_off2(); + } // Make sure w_topline is at the first of a sequence of folded lines. (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); validate_cursor(); // w_wrow needs to be valid - while (line_count-- > 0) { + for (long todo = line_count; todo > 0; todo--) { if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline) && curwin->w_topfill < curwin->w_height_inner - 1) { curwin->w_topfill++; done++; } else { - if (curwin->w_topline == 1) { + // break when at the very top + if (curwin->w_topline == 1 && (!do_sms || curwin->w_skipcol < width1)) { break; } - curwin->w_topline--; - curwin->w_topfill = 0; - // A sequence of folded lines only counts for one logical line - linenr_T first; - if (hasFolding(curwin->w_topline, &first, NULL)) { - done++; - if (!byfold) { - line_count -= curwin->w_topline - first - 1; + if (do_sms && curwin->w_skipcol >= width1) { + // scroll a screen line down + if (curwin->w_skipcol >= width1 + width2) { + curwin->w_skipcol -= width2; + } else { + curwin->w_skipcol -= width1; } - curwin->w_botline -= curwin->w_topline - first; - curwin->w_topline = first; + redraw_later(curwin, UPD_NOT_VALID); + done++; } else { - done += plines_win_nofill(curwin, curwin->w_topline, true); + // scroll a text line down + curwin->w_topline--; + curwin->w_skipcol = 0; + curwin->w_topfill = 0; + // A sequence of folded lines only counts for one logical line + linenr_T first; + if (hasFolding(curwin->w_topline, &first, NULL)) { + done++; + if (!byfold) { + todo -= curwin->w_topline - first - 1; + } + curwin->w_botline -= curwin->w_topline - first; + curwin->w_topline = first; + } else { + if (do_sms) { + int size = (int)win_linetabsize(curwin, curwin->w_topline, + ml_get(curwin->w_topline), (colnr_T)MAXCOL); + if (size > width1) { + curwin->w_skipcol = width1; + size -= width1; + redraw_later(curwin, UPD_NOT_VALID); + } + while (size > width2) { + curwin->w_skipcol += width2; + size -= width2; + } + done++; + } else { + done += plines_win_nofill(curwin, curwin->w_topline, true); + } + } } } curwin->w_botline--; // approximate w_botline @@ -1134,9 +1290,39 @@ bool scrolldown(long line_count, int byfold) foldAdjustCursor(); coladvance(curwin->w_curswant); } + + if (curwin->w_cursor.lnum == curwin->w_topline && do_sms) { + long so = get_scrolloff_value(curwin); + long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + + // make sure the cursor is in the visible text + validate_virtcol(); + long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; + int row = 0; + if (col >= width1) { + col -= width1; + row++; + } + if (col > width2 && width2 > 0) { + row += (int)col / width2; + col = col % width2; + } + if (row >= curwin->w_height_inner) { + curwin->w_curswant = curwin->w_virtcol - (row - curwin->w_height_inner + 1) * width2; + coladvance(curwin->w_curswant); + } + } return moved; } +/// Return TRUE if scrollup() will scroll by screen line rather than text line. +static int scrolling_screenlines(bool byfold) +{ + return (curwin->w_p_wrap && curwin->w_p_sms) + || (byfold && hasAnyFolding(curwin)) + || (curwin->w_p_diff && !curwin->w_p_wrap); +} + /// Scroll the current window up by "line_count" logical lines. "CTRL-E" /// /// @param line_count number of lines to scroll @@ -1145,28 +1331,68 @@ bool scrollup(long line_count, int byfold) { linenr_T topline = curwin->w_topline; linenr_T botline = curwin->w_botline; + int do_sms = curwin->w_p_wrap && curwin->w_p_sms; + + if (scrolling_screenlines(byfold) || win_may_fill(curwin)) { + int width1 = curwin->w_width_inner - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + unsigned size = 0; + linenr_T prev_topline = curwin->w_topline; + + if (do_sms) { + size = linetabsize(curwin, curwin->w_topline); + } - if ((byfold && hasAnyFolding(curwin)) - || win_may_fill(curwin)) { - // count each sequence of folded lines as one logical line - linenr_T lnum = curwin->w_topline; - while (line_count--) { + // diff mode: first consume "topfill" + // 'smoothscroll': increase "w_skipcol" until it goes over the end of + // the line, then advance to the next line. + // folding: count each sequence of folded lines as one logical line. + for (long todo = line_count; todo > 0; todo--) { if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { + linenr_T lnum = curwin->w_topline; if (byfold) { + // for a closed fold: go to the last line in the fold (void)hasFolding(lnum, NULL, &lnum); } - if (lnum >= curbuf->b_ml.ml_line_count) { - break; + if (lnum == curwin->w_topline && do_sms) { + // 'smoothscroll': increase "w_skipcol" until it goes over + // the end of the line, then advance to the next line. + int add = curwin->w_skipcol > 0 ? width2 : width1; + curwin->w_skipcol += add; + if ((unsigned)curwin->w_skipcol >= size) { + if (lnum == curbuf->b_ml.ml_line_count) { + // at the last screen line, can't scroll further + curwin->w_skipcol -= add; + break; + } + lnum++; + } + } else { + if (lnum >= curbuf->b_ml.ml_line_count) { + break; + } + lnum++; + } + + if (lnum > curwin->w_topline) { + // approximate w_botline + curwin->w_botline += lnum - curwin->w_topline; + curwin->w_topline = lnum; + curwin->w_topfill = win_get_fill(curwin, lnum); + curwin->w_skipcol = 0; + if (todo > 1 && do_sms) { + size = linetabsize(curwin, curwin->w_topline); + } } - lnum++; - curwin->w_topfill = win_get_fill(curwin, lnum); } } - // approximate w_botline - curwin->w_botline += lnum - curwin->w_topline; - curwin->w_topline = lnum; + + if (curwin->w_topline == prev_topline) { + // need to redraw even though w_topline didn't change + redraw_later(curwin, UPD_NOT_VALID); + } } else { curwin->w_topline += (linenr_T)line_count; curwin->w_botline += (linenr_T)line_count; // approximate w_botline @@ -1194,12 +1420,117 @@ bool scrollup(long line_count, int byfold) coladvance(curwin->w_curswant); } - bool moved = topline != curwin->w_topline - || botline != curwin->w_botline; + if (curwin->w_cursor.lnum == curwin->w_topline && do_sms && curwin->w_skipcol > 0) { + int col_off = curwin_col_off(); + int col_off2 = curwin_col_off2(); + + int width1 = curwin->w_width_inner - col_off; + int width2 = width1 + col_off2; + int extra2 = col_off - col_off2; + long so = get_scrolloff_value(curwin); + long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + int space_cols = (curwin->w_height_inner - 1) * width2; + + // If we have non-zero scrolloff, just ignore the <<< marker as we are + // going past it anyway. + int smoothscroll_overlap = scrolloff_cols != 0 ? 0 : + smoothscroll_marker_overlap(curwin, extra2); + + // Make sure the cursor is in a visible part of the line, taking + // 'scrolloff' into account, but using screen lines. + // If there are not enough screen lines put the cursor in the middle. + if (scrolloff_cols > space_cols / 2) { + scrolloff_cols = space_cols / 2; + } + validate_virtcol(); + if (curwin->w_virtcol < curwin->w_skipcol + smoothscroll_overlap + scrolloff_cols) { + colnr_T col = curwin->w_virtcol; + + if (col < width1) { + col += width1; + } + while (col < curwin->w_skipcol + smoothscroll_overlap + scrolloff_cols) { + col += width2; + } + curwin->w_curswant = col; + coladvance(curwin->w_curswant); + + // validate_virtcol() marked various things as valid, but after + // moving the cursor they need to be recomputed + curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); + } + } + + bool moved = topline != curwin->w_topline || botline != curwin->w_botline; return moved; } +/// Called after changing the cursor column: make sure that curwin->w_skipcol is +/// valid for 'smoothscroll'. +void adjust_skipcol(void) +{ + if (!curwin->w_p_wrap || !curwin->w_p_sms || curwin->w_cursor.lnum != curwin->w_topline) { + return; + } + + int width1 = curwin->w_width_inner - curwin_col_off(); + if (width1 <= 0) { + return; // no text will be displayed + } + int width2 = width1 + curwin_col_off2(); + long so = get_scrolloff_value(curwin); + long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + bool scrolled = false; + + validate_cheight(); + if (curwin->w_cline_height == curwin->w_height_inner + // w_cline_height may be capped at w_height_inner, check there aren't + // actually more lines. + && plines_win(curwin, curwin->w_cursor.lnum, false) <= curwin->w_height_inner) { + // the line just fits in the window, don't scroll + reset_skipcol(curwin); + return; + } + + validate_virtcol(); + while (curwin->w_skipcol > 0 + && curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { + // scroll a screen line down + if (curwin->w_skipcol >= width1 + width2) { + curwin->w_skipcol -= width2; + } else { + curwin->w_skipcol -= width1; + } + redraw_later(curwin, UPD_NOT_VALID); + scrolled = true; + validate_virtcol(); + } + if (scrolled) { + return; // don't scroll in the other direction now + } + long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; + int row = 0; + if (col >= width1) { + col -= width1; + row++; + } + if (col > width2) { + row += (int)col / width2; + col = col % width2; + } + if (row >= curwin->w_height_inner) { + if (curwin->w_skipcol == 0) { + curwin->w_skipcol += width1; + row--; + } + if (row >= curwin->w_height_inner) { + curwin->w_skipcol += (row - curwin->w_height_inner) * width2; + } + redraw_later(curwin, UPD_NOT_VALID); + } +} + /// Don't end up with too many filler lines in the window. /// /// @param down when true scroll down when not enough space @@ -1316,7 +1647,8 @@ void scrollup_clamp(void) // a (wrapped) text line. Uses and sets "lp->fill". // Returns the height of the added line in "lp->height". // Lines above the first one are incredibly high: MAXCOL. -static void topline_back(win_T *wp, lineoff_T *lp) +// When "winheight" is true limit to window height. +static void topline_back_winheight(win_T *wp, lineoff_T *lp, int winheight) { if (lp->fill < win_get_fill(wp, lp->lnum)) { // Add a filler line @@ -1331,11 +1663,16 @@ static void topline_back(win_T *wp, lineoff_T *lp) // Add a closed fold lp->height = 1; } else { - lp->height = plines_win_nofill(wp, lp->lnum, true); + lp->height = plines_win_nofill(wp, lp->lnum, winheight); } } } +static void topline_back(win_T *wp, lineoff_T *lp) +{ + topline_back_winheight(wp, lp, true); +} + // Add one line below "lp->lnum". This can be a filler line, a closed fold or // a (wrapped) text line. Uses and sets "lp->fill". // Returns the height of the added line in "lp->height". @@ -1388,12 +1725,9 @@ static void topline_botline(lineoff_T *lp) // If "always" is true, always set topline (for "zt"). void scroll_cursor_top(int min_scroll, int always) { - int scrolled = 0; - linenr_T top; // just above displayed lines - linenr_T bot; // just below displayed lines linenr_T old_topline = curwin->w_topline; + int old_skipcol = curwin->w_skipcol; linenr_T old_topfill = curwin->w_topfill; - linenr_T new_topline; int off = (int)get_scrolloff_value(curwin); if (mouse_dragging > 0) { @@ -1406,11 +1740,14 @@ void scroll_cursor_top(int min_scroll, int always) // - moved at least 'scrolljump' lines and // - at least 'scrolloff' lines above and below the cursor validate_cheight(); + int scrolled = 0; int used = curwin->w_cline_height; // includes filler lines above if (curwin->w_cursor.lnum < curwin->w_topline) { scrolled = used; } + linenr_T top; // just above displayed lines + linenr_T bot; // just below displayed lines if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) { top--; bot++; @@ -1418,7 +1755,7 @@ void scroll_cursor_top(int min_scroll, int always) top = curwin->w_cursor.lnum - 1; bot = curwin->w_cursor.lnum + 1; } - new_topline = top + 1; + linenr_T new_topline = top + 1; // "used" already contains the number of filler lines above, don't add it // again. @@ -1431,6 +1768,15 @@ void scroll_cursor_top(int min_scroll, int always) int i = hasFolding(top, &top, NULL) ? 1 // count one logical line for a sequence of folded lines : plines_win_nofill(curwin, top, true); + if (top < curwin->w_topline) { + scrolled += i; + } + + // If scrolling is needed, scroll at least 'sj' lines. + if ((new_topline >= curwin->w_topline || scrolled > min_scroll) && extra >= off) { + break; + } + used += i; if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) { if (hasFolding(bot, NULL, &bot)) { @@ -1443,15 +1789,6 @@ void scroll_cursor_top(int min_scroll, int always) if (used > curwin->w_height_inner) { break; } - if (top < curwin->w_topline) { - scrolled += i; - } - - // If scrolling is needed, scroll at least 'sj' lines. - if ((new_topline >= curwin->w_topline || scrolled > min_scroll) - && extra >= off) { - break; - } extra += i; new_topline = top; @@ -1466,7 +1803,7 @@ void scroll_cursor_top(int min_scroll, int always) scroll_cursor_halfway(false, false); } else { // If "always" is false, only adjust topline to a lower value, higher - // value may happen with wrapping lines + // value may happen with wrapping lines. if (new_topline < curwin->w_topline || always) { curwin->w_topline = new_topline; } @@ -1481,7 +1818,13 @@ void scroll_cursor_top(int min_scroll, int always) } } check_topfill(curwin, false); + // TODO(vim): if the line doesn't fit may optimize w_skipcol + if (curwin->w_topline == curwin->w_cursor.lnum + && curwin->w_skipcol >= curwin->w_cursor.col) { + reset_skipcol(curwin); + } if (curwin->w_topline != old_topline + || curwin->w_skipcol != old_skipcol || curwin->w_topfill != old_topfill) { curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); @@ -1519,20 +1862,18 @@ void set_empty_rows(win_T *wp, int used) void scroll_cursor_bot(int min_scroll, int set_topbot) { int used; - int scrolled = 0; - int extra = 0; lineoff_T loff; - lineoff_T boff; - int fill_below_window; - linenr_T old_topline = curwin->w_topline; - int old_topfill = curwin->w_topfill; - linenr_T old_botline = curwin->w_botline; - int old_valid = curwin->w_valid; + linenr_T old_topline = curwin->w_topline; + int old_skipcol = curwin->w_skipcol; + int old_topfill = curwin->w_topfill; + linenr_T old_botline = curwin->w_botline; + int old_valid = curwin->w_valid; int old_empty_rows = curwin->w_empty_rows; - linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number - long so = get_scrolloff_value(curwin); + linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number if (set_topbot) { + bool set_skipcol = false; + used = 0; curwin->w_botline = cln + 1; loff.fill = 0; @@ -1540,9 +1881,24 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) curwin->w_topline > 1; curwin->w_topline = loff.lnum) { loff.lnum = curwin->w_topline; - topline_back(curwin, &loff); - if (loff.height == MAXCOL - || used + loff.height > curwin->w_height_inner) { + topline_back_winheight(curwin, &loff, false); + if (loff.height == MAXCOL) { + break; + } + if (used + loff.height > curwin->w_height_inner) { + if (curwin->w_p_sms && curwin->w_p_wrap) { + // 'smoothscroll' and 'wrap' are set. The above line is + // too long to show in its entirety, so we show just a part + // of it. + if (used < curwin->w_height_inner) { + int plines_offset = used + loff.height - curwin->w_height_inner; + used = curwin->w_height_inner; + curwin->w_topfill = loff.fill; + curwin->w_topline = loff.lnum; + curwin->w_skipcol = skipcol_from_plines(curwin, plines_offset); + set_skipcol = true; + } + } break; } used += loff.height; @@ -1551,8 +1907,15 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) set_empty_rows(curwin, used); curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; if (curwin->w_topline != old_topline - || curwin->w_topfill != old_topfill) { + || curwin->w_topfill != old_topfill + || set_skipcol + || curwin->w_skipcol != 0) { curwin->w_valid &= ~(VALID_WROW|VALID_CROW); + if (set_skipcol) { + redraw_later(curwin, UPD_NOT_VALID); + } else { + reset_skipcol(curwin); + } } } else { validate_botline(curwin); @@ -1561,16 +1924,50 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) // The lines of the cursor line itself are always used. used = plines_win_nofill(curwin, cln, true); - // If the cursor is below botline, we will at least scroll by the height - // of the cursor line. Correct for empty lines, which are really part of - // botline. + int scrolled = 0; + int min_scrolled = 1; + // If the cursor is on or below botline, we will at least scroll by the + // height of the cursor line, which is "used". Correct for empty lines, + // which are really part of botline. if (cln >= curwin->w_botline) { scrolled = used; if (cln == curwin->w_botline) { scrolled -= curwin->w_empty_rows; } + min_scrolled = scrolled; + if (curwin->w_p_sms && curwin->w_p_wrap) { + // 'smoothscroll' and 'wrap' are set + if (cln > curwin->w_botline) { + // add screen lines below w_botline + for (linenr_T lnum = curwin->w_botline + 1; lnum <= cln; lnum++) { + min_scrolled += plines_win_nofill(curwin, lnum, true); + } + } + + // Calculate how many screen lines the current top line of window + // occupies. If it is occupying more than the entire window, we + // need to scroll the additional clipped lines to scroll past the + // top line before we can move on to the other lines. + int top_plines = plines_win_nofill(curwin, curwin->w_topline, false); + int skip_lines = 0; + int width1 = curwin->w_width_inner - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + // similar formula is used in curs_columns() + if (curwin->w_skipcol > width1) { + skip_lines += (curwin->w_skipcol - width1) / width2 + 1; + } else if (curwin->w_skipcol > 0) { + skip_lines = 1; + } + + top_plines -= skip_lines; + if (top_plines > curwin->w_height_inner) { + scrolled += (top_plines - curwin->w_height_inner); + min_scrolled += (top_plines - curwin->w_height_inner); + } + } } + lineoff_T boff; // Stop counting lines to scroll when // - hitting start of the file // - scrolled nothing or at least 'sj' lines @@ -1582,9 +1979,10 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } loff.fill = 0; boff.fill = 0; - fill_below_window = win_get_fill(curwin, curwin->w_botline) - - curwin->w_filler_rows; + int fill_below_window = win_get_fill(curwin, curwin->w_botline) - curwin->w_filler_rows; + int extra = 0; + long so = get_scrolloff_value(curwin); while (loff.lnum > 1) { // Stop when scrolled nothing or at least "min_scroll", found "extra" // context for 'scrolloff' and counted all lines below the window. @@ -1670,13 +2068,27 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) if (line_count >= curwin->w_height_inner && line_count > min_scroll) { scroll_cursor_halfway(false, true); } else { - scrollup(line_count, true); + // With 'smoothscroll' scroll at least the height of the cursor line, + // unless it would move the cursor. + if (curwin->w_p_wrap && curwin->w_p_sms && line_count < min_scrolled + && (curwin->w_cursor.lnum < curwin->w_topline + || (curwin->w_virtcol - curwin->w_skipcol >= + curwin->w_width_inner - curwin_col_off()))) { + line_count = min_scrolled; + } + if (line_count > 0) { + if (scrolling_screenlines(true)) { + scrollup(scrolled, true); // TODO(vim): + } else { + scrollup(line_count, true); + } + } } // If topline didn't change we need to restore w_botline and w_empty_rows // (we changed them). // If topline did change, update_screen() will set botline. - if (curwin->w_topline == old_topline && set_topbot) { + if (curwin->w_topline == old_topline && curwin->w_skipcol == old_skipcol && set_topbot) { curwin->w_botline = old_botline; curwin->w_empty_rows = old_empty_rows; curwin->w_valid = old_valid; @@ -1691,27 +2103,65 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) /// void scroll_cursor_halfway(bool atend, bool prefer_above) { - int above = 0; - int topfill = 0; - int below = 0; - lineoff_T loff; - lineoff_T boff; linenr_T old_topline = curwin->w_topline; - - loff.lnum = boff.lnum = curwin->w_cursor.lnum; + lineoff_T loff = { .lnum = curwin->w_cursor.lnum }; + lineoff_T boff = { .lnum = curwin->w_cursor.lnum }; (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum); int used = plines_win_nofill(curwin, loff.lnum, true); loff.fill = 0; boff.fill = 0; linenr_T topline = loff.lnum; + colnr_T skipcol = 0; + bool set_skipcol = false; + + int half_height = 0; + bool smooth_scroll = false; + if (curwin->w_p_sms && curwin->w_p_wrap) { + // 'smoothscroll' and 'wrap' are set + smooth_scroll = true; + half_height = (curwin->w_height_inner - used) / 2; + used = 0; + } + int topfill = 0; while (topline > 1) { + // If using smoothscroll, we can precisely scroll to the + // exact point where the cursor is halfway down the screen. + if (smooth_scroll) { + topline_back_winheight(curwin, &loff, false); + if (loff.height == MAXCOL) { + break; + } else { + used += loff.height; + } + if (used > half_height) { + if (used - loff.height < half_height) { + int plines_offset = used - half_height; + loff.height -= plines_offset; + used = half_height; + + topline = loff.lnum; + topfill = loff.fill; + skipcol = skipcol_from_plines(curwin, plines_offset); + set_skipcol = true; + } + break; + } + topline = loff.lnum; + topfill = loff.fill; + continue; + } + + // If not using smoothscroll, we have to iteratively find how many + // lines to scroll down to roughly fit the cursor. // This may not be right in the middle if the lines' // physical height > 1 (e.g. 'wrap' is on). // Depending on "prefer_above" we add a line above or below first. // Loop twice to avoid duplicating code. bool done = false; + int above = 0; + int below = 0; for (int round = 1; round <= 2; round++) { if (prefer_above ? (round == 2 && below < above) @@ -1757,8 +2207,15 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) } } - if (!hasFolding(topline, &curwin->w_topline, NULL)) { + if (!hasFolding(topline, &curwin->w_topline, NULL) + && (curwin->w_topline != topline || set_skipcol || curwin->w_skipcol != 0)) { curwin->w_topline = topline; + if (set_skipcol) { + curwin->w_skipcol = skipcol; + redraw_later(curwin, UPD_NOT_VALID); + } else { + reset_skipcol(curwin); + } } curwin->w_topfill = topfill; if (old_topline > curwin->w_topline + curwin->w_height_inner) { @@ -1809,6 +2266,16 @@ void cursor_correct(void) return; } + if (curwin->w_p_sms && !curwin->w_p_wrap) { + // 'smoothscroll is active + if (curwin->w_cline_height == curwin->w_height_inner) { + // The cursor line just fits in the window, don't scroll. + reset_skipcol(curwin); + return; + } + // TODO(vim): If the cursor line doesn't fit in the window then only adjust w_skipcol. + } + // Narrow down the area where the cursor can be put by taking lines from // the top and the bottom until: // - the desired context lines are found @@ -1861,9 +2328,9 @@ void cursor_correct(void) curwin->w_viewport_invalid = true; } -// move screen 'count' pages up or down and update screen +// Move screen "count" pages up or down and update screen. // -// return FAIL for failure, OK otherwise +// Return FAIL for failure, OK otherwise. int onepage(Direction dir, long count) { long n; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 5a5286905f..e39a5e1ab7 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2158,9 +2158,8 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) } // do the horizontal scroll - if (want_hor && curwin->w_leftcol != tgt_leftcol) { - curwin->w_leftcol = tgt_leftcol; - leftcol_changed(); + if (want_hor) { + (void)set_leftcol(tgt_leftcol); } } @@ -2432,7 +2431,7 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar /// @return true if able to move cursor, false otherwise. static bool nv_screengo(oparg_T *oap, int dir, long dist) { - int linelen = linetabsize(get_cursor_line_ptr()); + int linelen = linetabsize_str(get_cursor_line_ptr()); bool retval = true; bool atend = false; int col_off1; // margin offset for first screen line @@ -2494,7 +2493,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) retval = false; break; } - linelen = linetabsize(get_cursor_line_ptr()); + linelen = linetabsize_str(get_cursor_line_ptr()); if (linelen > width1) { int w = (((linelen - width1 - 1) / width2) + 1) * width2; assert(curwin->w_curswant <= INT_MAX - w); @@ -2525,7 +2524,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) if (curwin->w_curswant >= width1) { curwin->w_curswant -= width2; } - linelen = linetabsize(get_cursor_line_ptr()); + linelen = linetabsize_str(get_cursor_line_ptr()); } } } @@ -2566,6 +2565,8 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) if (atend) { curwin->w_curswant = MAXCOL; // stick in the last column } + adjust_skipcol(); + return retval; } @@ -2633,6 +2634,7 @@ static void nv_scroll_line(cmdarg_T *cap) void scroll_redraw(int up, long count) { linenr_T prev_topline = curwin->w_topline; + int prev_skipcol = curwin->w_skipcol; int prev_topfill = curwin->w_topfill; linenr_T prev_lnum = curwin->w_cursor.lnum; @@ -2640,7 +2642,7 @@ void scroll_redraw(int up, long count) scrollup(count, true) : scrolldown(count, true); - if (get_scrolloff_value(curwin)) { + if (get_scrolloff_value(curwin) > 0) { // Adjust the cursor position for 'scrolloff'. Mark w_topline as // valid, otherwise the screen jumps back at the end of the file. cursor_correct(); @@ -2651,6 +2653,7 @@ void scroll_redraw(int up, long count) // we get stuck at one position. Don't move the cursor up if the // first line of the buffer is already on the screen while (curwin->w_topline == prev_topline + && curwin->w_skipcol == prev_skipcol && curwin->w_topfill == prev_topfill) { if (up) { if (curwin->w_cursor.lnum > prev_lnum @@ -2890,27 +2893,21 @@ static void nv_zet(cmdarg_T *cap) case 'h': case K_LEFT: if (!curwin->w_p_wrap) { - if ((colnr_T)cap->count1 > curwin->w_leftcol) { - curwin->w_leftcol = 0; - } else { - curwin->w_leftcol -= (colnr_T)cap->count1; - } - leftcol_changed(); + (void)set_leftcol((colnr_T)cap->count1 > curwin->w_leftcol + ? 0 : curwin->w_leftcol - (colnr_T)cap->count1); } break; - // "zL" - scroll screen left half-page + // "zL" - scroll window left half-page case 'L': cap->count1 *= curwin->w_width_inner / 2; FALLTHROUGH; - // "zl" - scroll screen to the left + // "zl" - scroll window to the left if not wrapping case 'l': case K_RIGHT: if (!curwin->w_p_wrap) { - // scroll the window left - curwin->w_leftcol += (colnr_T)cap->count1; - leftcol_changed(); + (void)set_leftcol(curwin->w_leftcol + (colnr_T)cap->count1); } break; @@ -5349,7 +5346,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) colnr_T vcol; getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); - if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) { + if (vcol >= curwin->w_leftcol + curwin->w_width_inner - col_off) { curwin->w_cursor.col--; } } @@ -5493,7 +5490,7 @@ static void nv_g_cmd(cmdarg_T *cap) case 'M': oap->motion_type = kMTCharWise; oap->inclusive = false; - i = linetabsize(get_cursor_line_ptr()); + i = linetabsize_str(get_cursor_line_ptr()); if (cap->count0 > 0 && cap->count0 <= 100) { coladvance((colnr_T)(i * cap->count0 / 100)); } else { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 89fe9b464d..d8380303a3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -5503,7 +5503,7 @@ void cursor_pos_info(dict_T *dict) validate_virtcol(); col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); - col_print(buf2, sizeof(buf2), (int)strlen(p), linetabsize(p)); + col_print(buf2, sizeof(buf2), (int)strlen(p), linetabsize_str(p)); if (char_count_cursor == byte_count_cursor && char_count == byte_count) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 3264d80a2f..a977fc4f86 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2621,6 +2621,19 @@ static const char *did_set_showtabline(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +/// Process the updated 'smoothscroll' option value. +static const char *did_set_smoothscroll(optset_T *args FUNC_ATTR_UNUSED) +{ + win_T *win = (win_T *)args->os_win; + if (win->w_p_sms) { + return NULL; + } + + win->w_skipcol = 0; + changed_line_abv_curs_win(win); + return NULL; +} + /// Process the new 'foldlevel' option value. static const char *did_set_foldlevel(optset_T *args FUNC_ATTR_UNUSED) { @@ -4417,6 +4430,8 @@ static char *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return (char *)&(win->w_p_rlc); case PV_SCROLL: return (char *)&(win->w_p_scr); + case PV_SMS: + return (char *)&(win->w_p_sms); case PV_WRAP: return (char *)&(win->w_p_wrap); case PV_LBR: @@ -4648,6 +4663,7 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_briopt = copy_option_val(from->wo_briopt); to->wo_scb = from->wo_scb; to->wo_scb_save = from->wo_scb_save; + to->wo_sms = from->wo_sms; to->wo_crb = from->wo_crb; to->wo_crb_save = from->wo_crb_save; to->wo_spell = from->wo_spell; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 40e77550aa..944cc583b3 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -949,6 +949,7 @@ enum { WV_RLC, WV_SCBIND, WV_SCROLL, + WV_SMS, WV_SISO, WV_SO, WV_SPELL, diff --git a/src/nvim/options.lua b/src/nvim/options.lua index e028fbb6a6..c4a85969c0 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2012,6 +2012,15 @@ return { defaults={if_true=0} }, { + full_name='smoothscroll', abbreviation='sms', + short_desc=N_("scroll by screen lines when 'wrap' is set"), + type='bool', scope={'window'}, + pv_name='p_sms', + redraw={'current_window'}, + defaults={if_true=0}, + cb='did_set_smoothscroll' + }, + { full_name='scrollback', abbreviation='scbk', short_desc=N_("lines to scroll with CTRL-U and CTRL-D"), type='number', scope={'buffer'}, diff --git a/src/nvim/plines.c b/src/nvim/plines.c index b2a4ac710d..3e69e547cb 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -189,10 +189,11 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) /// @param[out] nextp if not NULL, the line after a fold /// @param[out] foldedp if not NULL, whether lnum is on a fold /// @param[in] cache whether to use the window's cache for folds +/// @param[in] winheight when true limit to window height /// /// @return the total number of screen lines int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const foldedp, - const bool cache) + const bool cache, const bool winheight) { bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL); if (foldedp) { @@ -201,9 +202,9 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const if (folded) { return 1; } else if (lnum == wp->w_topline) { - return plines_win_nofill(wp, lnum, true) + wp->w_topfill; + return plines_win_nofill(wp, lnum, winheight) + wp->w_topfill; } - return plines_win(wp, lnum, true); + return plines_win(wp, lnum, winheight); } int plines_m_win(win_T *wp, linenr_T first, linenr_T last) @@ -212,7 +213,7 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last) while (first <= last) { linenr_T next = first; - count += plines_win_full(wp, first, &next, NULL, false); + count += plines_win_full(wp, first, &next, NULL, false, true); first = next + 1; } return count; @@ -243,12 +244,12 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col) /// @param s /// /// @return Number of characters the string will take on the screen. -int linetabsize(char *s) +int linetabsize_str(char *s) { return linetabsize_col(0, s); } -/// Like linetabsize(), but "s" starts at column "startcol". +/// Like linetabsize_str(), but "s" starts at column "startcol". /// /// @param startcol /// @param s @@ -265,7 +266,7 @@ int linetabsize_col(int startcol, char *s) return cts.cts_vcol; } -/// Like linetabsize(), but for a given window instead of the current one. +/// Like linetabsize_str(), but for a given window instead of the current one. /// /// @param wp /// @param line @@ -284,6 +285,13 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) return (unsigned)cts.cts_vcol; } +/// Return the number of cells line "lnum" of window "wp" will take on the +/// screen, taking into account the size of a tab and text properties. +unsigned linetabsize(win_T *wp, linenr_T lnum) +{ + return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); +} + /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 3246ef2c71..d404aa9647 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -133,8 +133,6 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i validate_cursor_col(); int above_row = 0; int below_row = cmdline_row; - int row_off = 0; - int col_off = 0; // wildoptions=pum if (State == MODE_CMDLINE) { @@ -145,7 +143,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i // anchor position: the start of the completed word pum_win_row = curwin->w_wrow; if (pum_rl) { - cursor_col = curwin->w_width - curwin->w_wcol - 1; + cursor_col = curwin->w_width_inner - curwin->w_wcol - 1; } else { cursor_col = curwin->w_wcol; } @@ -153,10 +151,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i pum_anchor_grid = (int)curwin->w_grid.target->handle; pum_win_row += curwin->w_grid.row_offset; cursor_col += curwin->w_grid.col_offset; - if (ui_has(kUIMultigrid)) { - row_off = curwin->w_winrow; - col_off = curwin->w_wincol; - } else if (curwin->w_grid.target != &default_grid) { + if (!ui_has(kUIMultigrid) && curwin->w_grid.target != &default_grid) { pum_anchor_grid = (int)default_grid.handle; pum_win_row += curwin->w_winrow; cursor_col += curwin->w_wincol; @@ -202,6 +197,21 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } } + int min_row = 0; + int min_col = 0; + int max_col = Columns; + int win_start_col = curwin->w_wincol; + int win_end_col = W_ENDCOL(curwin); + if (!(State & MODE_CMDLINE) && ui_has(kUIMultigrid)) { + above_row -= curwin->w_winrow; + below_row = MAX(below_row - curwin->w_winrow, curwin->w_grid.rows); + min_row = -curwin->w_winrow; + min_col = -curwin->w_wincol; + max_col = MAX(Columns - curwin->w_wincol, curwin->w_grid.cols); + win_start_col = 0; + win_end_col = curwin->w_grid.cols; + } + // Figure out the size and position of the pum. if (size < PUM_DEF_HEIGHT) { pum_height = size; @@ -215,8 +225,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i // Put the pum below "pum_win_row" if possible. // If there are few lines decide on where there is more room. - if (pum_win_row + row_off + 2 >= below_row - pum_height - && pum_win_row + row_off - above_row > (below_row - above_row) / 2) { + if (pum_win_row + 2 >= below_row - pum_height + && pum_win_row - above_row > (below_row - above_row) / 2) { // pum above "pum_win_row" pum_above = true; @@ -232,12 +242,12 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } } - if (pum_win_row + row_off >= size + context_lines) { + if (pum_win_row - min_row >= size + context_lines) { pum_row = pum_win_row - size - context_lines; pum_height = size; } else { - pum_row = -row_off; - pum_height = pum_win_row + row_off - context_lines; + pum_row = min_row; + pum_height = pum_win_row - min_row - context_lines; } if (p_ph > 0 && pum_height > p_ph) { @@ -262,8 +272,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } pum_row = pum_win_row + context_lines; - if (size > below_row - row_off - pum_row) { - pum_height = below_row - row_off - pum_row; + if (size > below_row - pum_row) { + pum_height = below_row - pum_row; } else { pum_height = size; } @@ -279,9 +289,9 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } // If there is a preview window above avoid drawing over it. - if (pvwin != NULL && pum_row + row_off < above_row && pum_height > above_row) { - pum_row = above_row - row_off; - pum_height = pum_win_row + row_off - above_row; + if (pvwin != NULL && pum_row < above_row && pum_height > above_row) { + pum_row = above_row; + pum_height = pum_win_row - above_row; } pum_array = array; @@ -307,19 +317,19 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i def_width = max_width; } - if (((cursor_col + col_off < Columns - p_pw - || cursor_col + col_off < Columns - max_width) && !pum_rl) - || (pum_rl && (cursor_col + col_off > p_pw - || cursor_col + col_off > max_width))) { + if (((cursor_col < max_col - p_pw + || cursor_col < max_col - max_width) && !pum_rl) + || (pum_rl && (cursor_col - min_col > p_pw + || cursor_col - min_col > max_width))) { // align pum with "cursor_col" pum_col = cursor_col; // start with the maximum space available if (pum_rl) { - pum_width = pum_col + col_off - pum_scrollbar + 1; + pum_width = pum_col - min_col - pum_scrollbar + 1; } else { - assert(Columns - col_off - pum_col - pum_scrollbar >= 0); - pum_width = Columns - col_off - pum_col - pum_scrollbar; + assert(max_col - pum_col - pum_scrollbar >= 0); + pum_width = max_col - pum_col - pum_scrollbar; } if (pum_width > max_width + pum_kind_width + pum_extra_width + 1 @@ -331,42 +341,42 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i if (pum_width < p_pw) { pum_width = (int)p_pw; } - } else if (((cursor_col + col_off > p_pw - || cursor_col + col_off > max_width) && !pum_rl) - || (pum_rl && (cursor_col + col_off < Columns - p_pw - || cursor_col + col_off < Columns - max_width))) { + } else if (((cursor_col - min_col > p_pw + || cursor_col - min_col > max_width) && !pum_rl) + || (pum_rl && (cursor_col < max_col - p_pw + || cursor_col < max_col - max_width))) { // align pum edge with "cursor_col" - if (pum_rl && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1) { + if (pum_rl && win_end_col < max_width + pum_scrollbar + 1) { pum_col = cursor_col + max_width + pum_scrollbar + 1; - if (pum_col + col_off >= Columns) { - pum_col = Columns - col_off - 1; + if (pum_col >= max_col) { + pum_col = max_col - 1; } } else if (!pum_rl) { - if (curwin->w_wincol > Columns - max_width - pum_scrollbar + if (win_start_col > max_col - max_width - pum_scrollbar && max_width <= p_pw) { // use full width to end of the screen - pum_col = Columns - col_off - max_width - pum_scrollbar; - if (pum_col + col_off < 0) { - pum_col = -col_off; + pum_col = max_col - max_width - pum_scrollbar; + if (pum_col < min_col) { + pum_col = min_col; } } } if (pum_rl) { - pum_width = pum_col + col_off - pum_scrollbar + 1; + pum_width = pum_col - min_col - pum_scrollbar + 1; } else { - pum_width = Columns - col_off - pum_col - pum_scrollbar; + pum_width = max_col - pum_col - pum_scrollbar; } if (pum_width < p_pw) { pum_width = (int)p_pw; if (pum_rl) { - if (pum_width > pum_col + col_off) { - pum_width = pum_col + col_off; + if (pum_width > pum_col - min_col) { + pum_width = pum_col - min_col; } } else { - if (pum_width >= Columns - col_off - pum_col) { - pum_width = Columns - col_off - pum_col - 1; + if (pum_width >= max_col - pum_col) { + pum_width = max_col - pum_col - 1; } } } else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1 @@ -377,26 +387,23 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } } } - } else if (Columns < def_width) { + } else if (max_col - min_col < def_width) { // not enough room, will use what we have if (pum_rl) { - assert(Columns - col_off - 1 >= INT_MIN); - pum_col = Columns - col_off - 1; + pum_col = max_col - 1; } else { - pum_col = -col_off; + pum_col = min_col; } - pum_width = Columns - 1; + pum_width = max_col - min_col - 1; } else { if (max_width > p_pw) { // truncate max_width = (int)p_pw; } - if (pum_rl) { - pum_col = max_width - col_off - 1; + pum_col = min_col + max_width - 1; } else { - assert(Columns - max_width >= 0); - pum_col = Columns - col_off - max_width; + pum_col = max_col - max_width; } pum_width = max_width - pum_scrollbar; } @@ -435,18 +442,26 @@ void pum_redraw(void) int col_off = 0; bool extra_space = false; if (pum_rl) { - col_off = pum_width; - if (pum_col < curwin->w_wincol + curwin->w_width - 1) { + col_off = pum_width - 1; + assert(!(State & MODE_CMDLINE)); + int win_end_col = ui_has(kUIMultigrid) ? curwin->w_grid.cols : W_ENDCOL(curwin); + if (pum_col < win_end_col - 1) { grid_width += 1; extra_space = true; } - } else if (pum_col > 0) { - grid_width += 1; - col_off = 1; - extra_space = true; + } else { + int min_col = (!(State & MODE_CMDLINE) && ui_has(kUIMultigrid)) ? -curwin->w_wincol : 0; + if (pum_col > min_col) { + grid_width += 1; + col_off = 1; + extra_space = true; + } } if (pum_scrollbar > 0) { grid_width++; + if (pum_rl) { + col_off++; + } } grid_assign_handle(&pum_grid); @@ -974,42 +989,44 @@ void pum_set_event_info(dict_T *dict) static void pum_position_at_mouse(int min_width) { - int row_off = 0; - int col_off = 0; + int min_row = 0; + int max_row = Rows; + int max_col = Columns; if (mouse_grid > 1) { win_T *wp = get_win_by_grid_handle(mouse_grid); if (wp != NULL) { - row_off = wp->w_winrow; - col_off = wp->w_wincol; + min_row = -wp->w_winrow; + max_row = MAX(Rows - wp->w_winrow, wp->w_grid.rows); + max_col = MAX(Columns - wp->w_wincol, wp->w_grid.cols); } } pum_anchor_grid = mouse_grid; - if (Rows - row_off - mouse_row > pum_size) { + if (max_row - mouse_row > pum_size) { // Enough space below the mouse row. pum_above = false; pum_row = mouse_row + 1; - if (pum_height > Rows - row_off - pum_row) { - pum_height = Rows - row_off - pum_row; + if (pum_height > max_row - pum_row) { + pum_height = max_row - pum_row; } } else { // Show above the mouse row, reduce height if it does not fit. pum_above = true; pum_row = mouse_row - pum_size; - if (pum_row + row_off < 0) { - pum_height += pum_row + row_off; - pum_row = -row_off; + if (pum_row < min_row) { + pum_height += pum_row - min_row; + pum_row = min_row; } } - if (Columns - col_off - mouse_col >= pum_base_width - || Columns - col_off - mouse_col > min_width) { + if (max_col - mouse_col >= pum_base_width + || max_col - mouse_col > min_width) { // Enough space to show at mouse column. pum_col = mouse_col; } else { // Not enough space, right align with window. - pum_col = Columns - col_off - (pum_base_width > min_width ? min_width : pum_base_width); + pum_col = max_col - (pum_base_width > min_width ? min_width : pum_base_width); } - pum_width = Columns - col_off - pum_col; + pum_width = max_col - pum_col; if (pum_width > pum_base_width + 1) { pum_width = pum_base_width + 1; } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 0784c4c8ff..d1d1b9180f 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1206,8 +1206,8 @@ static void decor_spell_nav_start(win_T *wp) decor_redraw_reset(wp, &decor_state); } -static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col, - char **decor_error) +static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col, + char **decor_error) { if (*decor_lnum != lnum) { decor_providers_invoke_spell(wp, lnum - 1, col, lnum - 1, -1, decor_error); @@ -1215,7 +1215,7 @@ static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, *decor_lnum = lnum; } decor_redraw_col(wp, col, col, false, &decor_state); - return decor_state.spell == kTrue; + return decor_state.spell; } static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col) @@ -1352,9 +1352,18 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att : p - buf) > wp->w_cursor.col)) { col = (colnr_T)(p - buf); - bool can_spell = (!has_syntax && (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0) - || decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error) - || (has_syntax && can_syn_spell(wp, lnum, col)); + bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0; + bool can_spell = !no_plain_buffer; + switch (decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error)) { + case kTrue: + can_spell = true; break; + case kFalse: + can_spell = false; break; + case kNone: + if (has_syntax) { + can_spell = can_syn_spell(wp, lnum, col); + } + } if (!can_spell) { attr = HLF_COUNT; diff --git a/test/functional/legacy/conceal_spec.lua b/test/functional/legacy/conceal_spec.lua index 429cf9dc03..6aaa93f886 100644 --- a/test/functional/legacy/conceal_spec.lua +++ b/test/functional/legacy/conceal_spec.lua @@ -474,6 +474,39 @@ describe('Conceal', function() ]]) end) + -- oldtest: Test_conceal_linebreak() + it('with linebreak', function() + local screen = Screen.new(75, 8) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + }) + screen:attach() + exec([[ + let &wrap = v:true + let &conceallevel = 2 + let &concealcursor = 'nc' + let &linebreak = v:true + let &showbreak = '+ ' + let line = 'a`a`a`a`' + \ .. 'a'->repeat(&columns - 15) + \ .. ' b`b`' + \ .. 'b'->repeat(&columns - 10) + \ .. ' cccccc' + eval ['x'->repeat(&columns), '', line]->setline(1) + syntax region CodeSpan matchgroup=Delimiter start=/\z(`\+\)/ end=/\z1/ concealends + ]]) + screen:expect([[ + ^xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| + | + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {0:+ }bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + {0:+ }cccccc | + {0:~ }| + {0:~ }| + | + ]]) + end) + -- Tests for correct display (cursor column position) with +conceal and tabulators. -- oldtest: Test_conceal_cursor_pos() it('cursor and column position with conceal and tabulators', function() diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua index f9b78f5dcd..f1cd8d1aac 100644 --- a/test/functional/legacy/display_spec.lua +++ b/test/functional/legacy/display_spec.lua @@ -194,4 +194,51 @@ describe('display', function() it('display "lastline" works correctly with multibyte fillchar', function() run_test_display_lastline(true) end) + + -- oldtest: Test_display_long_lastline() + it('display "lastline" shows correct text when end of wrapped line is deleted', function() + local screen = Screen.new(35, 14) + screen:attach() + exec([[ + set display=lastline scrolloff=5 + call setline(1, [ + \'aaaaa'->repeat(100), + \'bbbbb '->repeat(7) .. 'ccccc '->repeat(7) .. 'ddddd '->repeat(7) + \]) + ]]) + feed('482|') + screen:expect([[ + <<<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaa| + aaaaaaaaaa | + | + ]]) + feed('D') + screen:expect([[ + <<<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaa^a | + bbbbb bbbbb bbbbb bbbbb bbbbb bb@@@| + | + ]]) + end) end) diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua new file mode 100644 index 0000000000..8af23d2c26 --- /dev/null +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -0,0 +1,825 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local exec = helpers.exec +local feed = helpers.feed + +before_each(clear) + +describe('smoothscroll', function() + local screen + + before_each(function() + screen = Screen.new(40, 12) + screen:attach() + end) + + -- oldtest: Test_CtrlE_CtrlY_stop_at_end() + it('disabled does not break <C-E> and <C-Y> stop at end', function() + exec([[ + enew + call setline(1, ['one', 'two']) + set number + ]]) + feed('<C-Y>') + screen:expect({any = " 1 ^one"}) + feed('<C-E><C-E><C-E>') + screen:expect({any = " 2 ^two"}) + end) + + -- oldtest: Test_smoothscroll_CtrlE_CtrlY() + it('works with <C-E> and <C-E>', function() + exec([[ + call setline(1, [ 'line one', 'word '->repeat(20), 'line three', 'long word '->repeat(7), 'line', 'line', 'line', ]) + set smoothscroll scrolloff=5 + :5 + ]]) + local s1 = [[ + word word word word word word word word | + word word word word word word word word | + word word word word | + line three | + long word long word long word long word | + long word long word long word | + ^line | + line | + line | + ~ | + ~ | + | + ]] + local s2 = [[ + <<<d word word word word word word word | + word word word word | + line three | + long word long word long word long word | + long word long word long word | + ^line | + line | + line | + ~ | + ~ | + ~ | + | + ]] + local s3 = [[ + <<<d word word word | + line three | + long word long word long word long word | + long word long word long word | + ^line | + line | + line | + ~ | + ~ | + ~ | + ~ | + | + ]] + local s4 = [[ + line three | + long word long word long word long word | + long word long word long word | + line | + line | + ^line | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]] + local s5 = [[ + <<<d word word word | + line three | + long word long word long word long word | + long word long word long word | + line | + line | + ^line | + ~ | + ~ | + ~ | + ~ | + | + ]] + local s6 = [[ + <<<d word word word word word word word | + word word word word | + line three | + long word long word long word long word | + long word long word long word | + line | + line | + ^line | + ~ | + ~ | + ~ | + | + ]] + local s7 = [[ + word word word word word word word word | + word word word word word word word word | + word word word word | + line three | + long word long word long word long word | + long word long word long word | + line | + line | + ^line | + ~ | + ~ | + | + ]] + local s8 = [[ + line one | + word word word word word word word word | + word word word word word word word word | + word word word word | + line three | + long word long word long word long word | + long word long word long word | + line | + line | + ^line | + ~ | + | + ]] + feed('<C-E>') + screen:expect(s1) + feed('<C-E>') + screen:expect(s2) + feed('<C-E>') + screen:expect(s3) + feed('<C-E>') + screen:expect(s4) + feed('<C-Y>') + screen:expect(s5) + feed('<C-Y>') + screen:expect(s6) + feed('<C-Y>') + screen:expect(s7) + feed('<C-Y>') + screen:expect(s8) + exec('set foldmethod=indent') + -- move the cursor so we can reuse the same dumps + feed('5G<C-E>') + screen:expect(s1) + feed('<C-E>') + screen:expect(s2) + feed('7G<C-Y>') + screen:expect(s7) + feed('<C-Y>') + screen:expect(s8) + end) + + -- oldtest: Test_smoothscroll_number() + it("works 'number' and 'cpo'+=n", function() + exec([[ + call setline(1, [ 'one ' .. 'word '->repeat(20), 'two ' .. 'long word '->repeat(7), 'line', 'line', 'line', ]) + set smoothscroll scrolloff=5 + set splitkeep=topline + set number cpo+=n + :3 + func g:DoRel() + set number relativenumber scrolloff=0 + :%del + call setline(1, [ 'one', 'very long text '->repeat(12), 'three', ]) + exe "normal 2Gzt\<C-E>" + endfunc + ]]) + screen:expect([[ + 1 one word word word word word word wo| + rd word word word word word word word wo| + rd word word word word word | + 2 two long word long word long word lo| + ng word long word long word long word | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + ~ | + | + ]]) + feed('<C-E>') + screen:expect([[ + <<<word word word word word word word wo| + rd word word word word word | + 2 two long word long word long word lo| + ng word long word long word long word | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + ~ | + ~ | + | + ]]) + feed('<C-E>') + screen:expect([[ + <<<word word word word word | + 2 two long word long word long word lo| + ng word long word long word long word | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + exec('set cpo-=n') + screen:expect([[ + <<< d word word word word word word | + 2 two long word long word long word lo| + ng word long word long word long wor| + d | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + ~ | + ~ | + | + ]]) + feed('<C-Y>') + screen:expect([[ + <<< rd word word word word word word wor| + d word word word word word word | + 2 two long word long word long word lo| + ng word long word long word long wor| + d | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + ~ | + | + ]]) + feed('<C-Y>') + screen:expect([[ + 1 one word word word word word word wo| + rd word word word word word word wor| + d word word word word word word | + 2 two long word long word long word lo| + ng word long word long word long wor| + d | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + | + ]]) + exec('botright split') + feed('gg') + screen:expect([[ + 1 one word word word word word word wo| + rd word word word word word word wor| + d word word word word word word | + 2 two long word long word long word@@@| + [No Name] [+] | + 1 ^one word word word word word word wo| + rd word word word word word word wor| + d word word word word word word | + 2 two long word long word long word lo| + ng word long word long word long @@@| + [No Name] [+] | + | + ]]) + feed('<C-E>') + screen:expect([[ + 1 one word word word word word word wo| + rd word word word word word word wor| + d word word word word word word | + 2 two long word long word long word@@@| + [No Name] [+] | + <<< rd word word word word word word wor| + d word word word word word word^ | + 2 two long word long word long word lo| + ng word long word long word long wor| + d | + [No Name] [+] | + | + ]]) + feed('<C-E>') + screen:expect([[ + 1 one word word word word word word wo| + rd word word word word word word wor| + d word word word word word word | + 2 two long word long word long word@@@| + [No Name] [+] | + <<< d word word word word word word^ | + 2 two long word long word long word lo| + ng word long word long word long wor| + d | + 3 line | + [No Name] [+] | + | + ]]) + exec('close') + exec('call DoRel()') + screen:expect([[ + 2<<<^ong text very long text very long te| + xt very long text very long text ver| + y long text very long text very long| + text very long text very long text | + 1 three | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + --No lines in buffer-- | + ]]) + end) + + -- oldtest: Test_smoothscroll_list() + it("works with list mode", function() + screen:try_resize(40, 8) + exec([[ + set smoothscroll scrolloff=0 + set list + call setline(1, [ 'one', 'very long text '->repeat(12), 'three', ]) + exe "normal 2Gzt\<C-E>" + ]]) + screen:expect([[ + <<<t very long text very long text very | + ^long text very long text very long text | + very long text very long text very long | + text very long text- | + three | + ~ | + ~ | + | + ]]) + exec('set listchars+=precedes:#') + screen:expect([[ + #ext very long text very long text very | + ^long text very long text very long text | + very long text very long text very long | + text very long text- | + three | + ~ | + ~ | + | + ]]) + end) + + -- oldtest: Test_smoothscroll_diff_mode() + it("works with diff mode", function() + screen:try_resize(40, 8) + exec([[ + let text = 'just some text here' + call setline(1, text) + set smoothscroll + diffthis + new + call setline(1, text) + set smoothscroll + diffthis + ]]) + screen:expect([[ + - ^just some text here | + ~ | + ~ | + [No Name] [+] | + - just some text here | + ~ | + [No Name] [+] | + | + ]]) + feed('<C-Y>') + screen:expect_unchanged() + feed('<C-E>') + screen:expect_unchanged() + end) + + -- oldtest: Test_smoothscroll_wrap_scrolloff_zero() + it("works with zero 'scrolloff'", function() + screen:try_resize(40, 8) + exec([[ + call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) + set smoothscroll scrolloff=0 display= + :3 + ]]) + screen:expect([[ + <<<h some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + ^Line with some text with some text with | + some text with some text with some text | + with some text with some text | + | + ]]) + feed('j') + screen:expect_unchanged() + -- moving cursor down - whole bottom line shows + feed('<C-E>j') + screen:expect_unchanged() + feed('G') + screen:expect_unchanged() + -- moving cursor up right after the >>> marker - no need to show whole line + feed('2gj3l2k') + screen:expect([[ + <<<^h some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + | + ]]) + -- moving cursor up where the >>> marker is - whole top line shows + feed('2j02k') + screen:expect([[ + ^Line with some text with some text with | + some text with some text with some text | + with some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + @ | + | + ]]) + end) + + -- oldtest: Test_smoothscroll_wrap_long_line() + it("adjusts the cursor position in a long line", function() + screen:try_resize(40, 6) + exec([[ + call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four']) + set smoothscroll scrolloff=0 + normal 3G10|zt + ]]) + -- scrolling up, cursor moves screen line down + screen:expect([[ + Line with^ lots of text with lots of text| + with lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text with lots of te| + | + ]]) + feed('<C-E>') + screen:expect([[ + <<<th lot^s of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text with lots of te| + xt with lots of text with lots of text w| + | + ]]) + feed('5<C-E>') + screen:expect([[ + <<< lots ^of text with lots of text with | + lots of text with lots of text with lots| + of text with lots of text with lots of | + text with lots of text with lots of text| + with lots of text with lots of text wit| + | + ]]) + -- scrolling down, cursor moves screen line up + feed('5<C-Y>') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text with lots of te| + xt with l^ots of text with lots of text w| + | + ]]) + feed('<C-Y>') + screen:expect([[ + Line with lots of text with lots of text| + with lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text wi^th lots of text with lots of te| + | + ]]) + -- 'scrolloff' set to 1, scrolling up, cursor moves screen line down + exec('set scrolloff=1') + feed('10|<C-E>') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of^ text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text with lots of te| + xt with lots of text with lots of text w| + | + ]]) + -- 'scrolloff' set to 1, scrolling down, cursor moves screen line up + feed('<C-E>gjgj<C-Y>') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text wi^th lots of text with lots of te| + xt with lots of text with lots of text w| + | + ]]) + -- 'scrolloff' set to 2, scrolling up, cursor moves screen line down + exec('set scrolloff=2') + feed('10|<C-E>') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of tex^t with lots of text with lots o| + f text with lots of text with lots of te| + xt with lots of text with lots of text w| + | + ]]) + -- 'scrolloff' set to 2, scrolling down, cursor moves screen line up + feed('<C-E>gj<C-Y>') + screen:expect_unchanged() + -- 'scrolloff' set to 0, move cursor down one line. Cursor should move properly, + -- and since this is a really long line, it will be put on top of the screen. + exec('set scrolloff=0') + feed('0j') + screen:expect([[ + <<<of text with lots of text with lots o| + f text with lots of text end | + ^four | + ~ | + ~ | + | + ]]) + -- Test zt/zz/zb that they work properly when a long line is above it + feed('zb') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text end | + ^four | + | + ]]) + feed('zz') + screen:expect([[ + <<<of text with lots of text with lots o| + f text with lots of text end | + ^four | + ~ | + ~ | + | + ]]) + feed('zt') + screen:expect([[ + ^four | + ~ | + ~ | + ~ | + ~ | + | + ]]) + -- Repeat the step and move the cursor down again. + -- This time, use a shorter long line that is barely long enough to span more + -- than one window. Note that the cursor is at the bottom this time because + -- Vim prefers to do so if we are scrolling a few lines only. + exec("call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])") + feed('3Gztj') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text end | + ^four | + | + ]]) + -- Repeat the step but this time start it when the line is smooth-scrolled by + -- one line. This tests that the offset calculation is still correct and + -- still end up scrolling down to the next line with cursor at bottom of + -- screen. + feed('3Gzt<C-E>j') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text end | + fou^r | + | + ]]) + end) + + -- oldtest: Test_smoothscroll_one_long_line() + it("scrolls correctly when moving the cursor", function() + screen:try_resize(40, 6) + exec([[ + call setline(1, 'with lots of text '->repeat(7)) + set smoothscroll scrolloff=0 + ]]) + local s1 = [[ + ^with lots of text with lots of text with| + lots of text with lots of text with lot| + s of text with lots of text with lots of| + text | + ~ | + | + ]] + screen:expect(s1) + feed('<C-E>') + screen:expect([[ + <<<ts of text with lots of text with lot| + ^s of text with lots of text with lots of| + text | + ~ | + ~ | + | + ]]) + feed('0') + screen:expect(s1) + end) + + -- oldtest: Test_smoothscroll_long_line_showbreak() + it("cursor is not one screen line too far down", function() + screen:try_resize(40, 6) + -- a line that spans four screen lines + exec("call setline(1, 'with lots of text in one line '->repeat(6))") + exec('set smoothscroll scrolloff=0 showbreak=+++\\ ') + local s1 = [[ + ^with lots of text in one line with lots | + +++ of text in one line with lots of tex| + +++ t in one line with lots of text in o| + +++ ne line with lots of text in one lin| + +++ e with lots of text in one line | + | + ]] + screen:expect(s1) + feed('<C-E>') + screen:expect([[ + +++ ^of text in one line with lots of tex| + +++ t in one line with lots of text in o| + +++ ne line with lots of text in one lin| + +++ e with lots of text in one line | + ~ | + | + ]]) + feed('0') + screen:expect(s1) + end) + + -- oldtest: Test_smoothscroll_zero_width() + it("does not divide by zero with a narrow window", function() + screen:try_resize(12, 2) + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.Brown}, + [2] = {foreground = Screen.colors.Blue1, bold = true}, + }) + exec([[ + call setline(1, ['a'->repeat(100)]) + set wrap smoothscroll number laststatus=0 + wincmd v + wincmd v + wincmd v + wincmd v + ]]) + screen:expect([[ + {1: 1^ }│{1: }│{1: }│{1: }│{1: }| + | + ]]) + feed('llllllllll<C-W>o') + screen:expect([[ + {2:<<<}{1: }aa^aaaaaa| + | + ]]) + end) + + it("works with virt_lines above and below", function() + screen:try_resize(55, 7) + exec([=[ + call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(3)) + set smoothscroll + let ns = nvim_create_namespace('') + call nvim_buf_set_extmark(0, ns, 0, 0, {'virt_lines':[[['virt_below1']]]}) + call nvim_buf_set_extmark(0, ns, 1, 0, {'virt_lines':[[['virt_above1']]],'virt_lines_above':1}) + call nvim_buf_set_extmark(0, ns, 1, 0, {'virt_lines':[[['virt_below2']]]}) + call nvim_buf_set_extmark(0, ns, 2, 0, {'virt_lines':[[['virt_above2']]],'virt_lines_above':1}) + norm ggL + ]=]) + screen:expect([[ + Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below1 | + virt_above1 | + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + | + ]]) + feed('<C-E>') + screen:expect([[ + <<<e text with some text with some text with some text | + virt_below1 | + virt_above1 | + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below2 | + | + ]]) + feed('<C-E>') + screen:expect([[ + virt_below1 | + virt_above1 | + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below2 | + virt_above2 | + | + ]]) + feed('<C-E>') + screen:expect([[ + virt_above1 | + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below2 | + virt_above2 | + Line with some text with some text with some text wi@@@| + | + ]]) + feed('<C-E>') + screen:expect([[ + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below2 | + virt_above2 | + Line with some text with some text with some text with | + some text with some text with some text with some text | + | + ]]) + feed('<C-E>') + screen:expect([[ + <<<e text with some text with some text with some tex^t | + virt_below2 | + virt_above2 | + Line with some text with some text with some text with | + some text with some text with some text with some text | + ~ | + | + ]]) + end) + + it('<<< marker shows with tabline, winbar and splits', function() + screen:try_resize(40, 12) + exec([[ + call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) + set smoothscroll scrolloff=0 + norm sj + ]]) + screen:expect([[ + <<<e text with some text with some text | + with some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + [No Name] [+] | + <<<e text with some text with some text | + ^with some text with some text | + Line with some text with some text with | + some text with some text with some te@@@| + [No Name] [+] | + | + ]]) + exec('set showtabline=2') + feed('<C-E>') + screen:expect([[ + 2+ [No Name] | + <<<e text with some text with some text | + with some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + [No Name] [+] | + <<<e text with some text with some text | + ^with some text with some text | + Line with some text with some text wi@@@| + [No Name] [+] | + | + ]]) + exec('set winbar=winbar') + feed('<C-w>k<C-E>') + screen:expect([[ + 2+ [No Name] | + winbar | + <<<e text with some text with some text | + ^with some text with some text | + Line with some text with some text with | + some text with some text with some te@@@| + [No Name] [+] | + winbar | + <<<e text with some text with some text | + with some text with some text | + [No Name] [+] | + | + ]]) + end) +end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 5792c9703d..f531878bc6 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -8,6 +8,7 @@ local exec_lua = helpers.exec_lua local exec = helpers.exec local expect_events = helpers.expect_events local meths = helpers.meths +local curbufmeths = helpers.curbufmeths local command = helpers.command describe('decorations providers', function() @@ -31,8 +32,9 @@ describe('decorations providers', function() [12] = {foreground = tonumber('0x990000')}; [13] = {background = Screen.colors.LightBlue}; [14] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue}; - [15] = {special = Screen.colors.Blue1, undercurl = true}, + [15] = {special = Screen.colors.Blue, undercurl = true}, [16] = {special = Screen.colors.Red, undercurl = true}, + [17] = {foreground = Screen.colors.Red}, } end) @@ -201,14 +203,14 @@ describe('decorations providers', function() feed "gg0" screen:expect{grid=[[ - ^I am well written text. | - {15:i} am not capitalized. | - I am a {16:speling} {16:mistakke}. | - | - {1:~ }| - {1:~ }| - {1:~ }| - | + ^I am well written text. | + {15:i} am not capitalized. | + I am a {16:speling} {16:mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | ]]} feed "]s" @@ -216,14 +218,14 @@ describe('decorations providers', function() { "spell", 1000, 1, 1, 0, 1, -1 }; } screen:expect{grid=[[ - I am well written text. | - {15:^i} am not capitalized. | - I am a {16:speling} {16:mistakke}. | - | - {1:~ }| - {1:~ }| - {1:~ }| - | + I am well written text. | + {15:^i} am not capitalized. | + I am a {16:speling} {16:mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | ]]} feed "]s" @@ -231,43 +233,68 @@ describe('decorations providers', function() { "spell", 1000, 1, 2, 7, 2, -1 }; } screen:expect{grid=[[ - I am well written text. | - {15:i} am not capitalized. | - I am a {16:^speling} {16:mistakke}. | - | - {1:~ }| - {1:~ }| - {1:~ }| - | + I am well written text. | + {15:i} am not capitalized. | + I am a {16:^speling} {16:mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | ]]} - -- spell=false with lower priority doesn't disable spell + -- spell=false with higher priority does disable spell local ns = meths.create_namespace "spell" - local id = helpers.curbufmeths.set_extmark(ns, 0, 0, { priority = 30, end_row = 2, end_col = 23, spell = false }) + local id = curbufmeths.set_extmark(ns, 0, 0, { priority = 30, end_row = 2, end_col = 23, spell = false }) screen:expect{grid=[[ - I am well written text. | - i am not capitalized. | - I am a ^speling mistakke. | - | - {1:~ }| - {1:~ }| - {1:~ }| - | + I am well written text. | + i am not capitalized. | + I am a ^speling mistakke. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | ]]} - -- spell=false with higher priority does disable spell - helpers.curbufmeths.set_extmark(ns, 0, 0, { id = id, priority = 10, end_row = 2, end_col = 23, spell = false }) + feed "]s" + screen:expect{grid=[[ + I am well written text. | + i am not capitalized. | + I am a ^speling mistakke. | + | + {1:~ }| + {1:~ }| + {1:~ }| + {17:search hit BOTTOM, continuing at TOP} | + ]]} + command('echo ""') + + -- spell=false with lower priority doesn't disable spell + curbufmeths.set_extmark(ns, 0, 0, { id = id, priority = 10, end_row = 2, end_col = 23, spell = false }) + + screen:expect{grid=[[ + I am well written text. | + {15:i} am not capitalized. | + I am a {16:^speling} {16:mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + feed "]s" screen:expect{grid=[[ - I am well written text. | - {15:i} am not capitalized. | - I am a {16:^speling} {16:mistakke}. | - | - {1:~ }| - {1:~ }| - {1:~ }| - | + I am well written text. | + {15:i} am not capitalized. | + I am a {16:speling} {16:^mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | ]]} end) diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index dbdf3823ec..0f551e3044 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -1325,6 +1325,7 @@ it('win_update redraws lines properly', function() ]]} end) +-- oldtest: Test_diff_rnu() it('diff updates line numbers below filler lines', function() clear() local screen = Screen.new(40, 14) @@ -1401,6 +1402,7 @@ it('diff updates line numbers below filler lines', function() ]]) end) +-- oldtest: Test_diff_with_scroll_and_change() it('Align the filler lines when changing text in diff mode', function() clear() local screen = Screen.new(40, 20) diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index d9b9cf9f1b..2c0a00c74f 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -1861,5 +1861,16 @@ describe('ui/mouse/input', function() feed('<Down><CR>') eq({1, 9}, meths.win_get_cursor(0)) eq('ran away', funcs.getreg('"')) + + -- Test for right click inside visual selection at bottom of window with winbar + command('setlocal winbar=WINBAR') + feed('2yyP') + funcs.setreg('"', '') + feed('G$vbb') + meths.input_mouse('right', 'press', '', 0, 4, 61) + meths.input_mouse('right', 'release', '', 0, 4, 61) + feed('<Down><CR>') + eq({4, 20}, meths.win_get_cursor(0)) + eq('the moon', funcs.getreg('"')) end) end) diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index 2525314b8e..4c04bcb54e 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -37,6 +37,10 @@ describe('ext_multigrid', function() [18] = {bold = true, foreground = Screen.colors.Magenta}, [19] = {foreground = Screen.colors.Brown}, [20] = {background = Screen.colors.LightGrey}, + [21] = {background = Screen.colors.LightMagenta}, + [22] = {background = Screen.colors.LightMagenta, bold = true, foreground = Screen.colors.Blue}, + [23] = {background = Screen.colors.Grey90}, + [24] = {background = Screen.colors.Grey}, }) end) @@ -884,7 +888,6 @@ describe('ext_multigrid', function() it('gets written till grid width', function() insert(('a'):rep(60).."\n") - screen:expect{grid=[[ ## grid 1 [2:-----------------------------------------------------]| @@ -927,8 +930,95 @@ describe('ext_multigrid', function() ]]} end) + it('"g$" works correctly with double-width characters and no wrapping', function() + command('set nowrap') + insert(('a'):rep(58) .. ('哦'):rep(3)) + feed('0') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa哦| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ]]} + feed('g$') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^哦| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ]]} + end) + it('wraps with grid width', function() - insert(('b'):rep(80).."\n") + insert(('b'):rep(160).."\n") screen:expect{grid=[[ ## grid 1 [2:-----------------------------------------------------]| @@ -947,7 +1037,8 @@ describe('ext_multigrid', function() [3:-----------------------------------------------------]| ## grid 2 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| - bbbbbbbbbbbbbbbbbbbb | + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | ^ | {1:~ }| {1:~ }| @@ -965,6 +1056,47 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| {1:~ }| + ## grid 3 + | + ]]} + feed('2gk') + command('setlocal cursorline cursorlineopt=screenline') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + {23:^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| {1:~ }| ## grid 3 | @@ -1060,6 +1192,255 @@ describe('ext_multigrid', function() | ]]} end) + + it('anchored float window "bufpos"', function() + insert(('c'):rep(1111)) + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccc^c | + {1:~ }| + ## grid 3 + | + ]]} + local float_buf = meths.create_buf(false, false) + meths.open_win(float_buf, false, { + relative = 'win', + win = curwin(), + bufpos = {0, 1018}, + anchor = 'SE', + width = 5, + height = 5, + }) + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| + cccccccccccccccccccccccccccccc^c | + {1:~ }| + ## grid 3 + | + ## grid 4 + {21: }| + {22:~ }| + {22:~ }| + {22:~ }| + {22:~ }| + ]], float_pos={ + [4] = {{id = 1001}, "SE", 2, 16, 58, true, 50}; + }} + end) + + it('completion popup position', function() + insert(('\n'):rep(14) .. ('foo bar '):rep(7)) + feed('A<C-X><C-N>') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + | + | + | + | + | + | + | + | + | + | + | + | + | + | + foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- Keyword Local completion (^N^P) }{15:match 1 of 2} | + ## grid 4 + {24: foo}| + {21: bar}| + ]], float_pos={ + [4] = {{id = -1}, "NW", 2, 15, 55, false, 100}; + }} + feed('<C-E><Esc>') + + command('setlocal rightleft') + feed('o<C-X><C-N>') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + | + | + | + | + | + | + | + | + | + | + | + | + | + | + rab oof rab oof rab oof rab oof rab oof rab oof rab oof| + ^ oof| + {1: ~}| + {1: ~}| + {1: ~}| + {1: ~}| + ## grid 3 + {7:-- Keyword Local completion (^N^P) }{15:match 1 of 2} | + ## grid 4 + {24: oof}| + {21: rab}| + ]], float_pos={ + [4] = {{id = -1}, "NW", 2, 16, 45, false, 100}; + }} + feed('<C-E><Esc>') + + command('set wildoptions+=pum') + feed(':sign un<Tab>') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + | + | + | + | + | + | + | + | + | + | + | + | + | + | + rab oof rab oof rab oof rab oof rab oof rab oof rab oof| + | + {1: ~}| + {1: ~}| + {1: ~}| + {1: ~}| + ## grid 3 + :sign undefine^ | + ## grid 4 + {24: undefine }| + {21: unplace }| + ]], float_pos={ + [4] = {{id = -1}, "SW", 1, 13, 5, false, 250}; + }} + end) end) it('multiline messages scroll over windows', function() @@ -2003,7 +2384,7 @@ describe('ext_multigrid', function() {1:~ }| ]]} - meths.input_mouse('left', 'press', '', 1,8, 26) + meths.input_mouse('left', 'press', '', 1, 8, 26) poke_eventloop() meths.input_mouse('left', 'drag', '', 1, 6, 30) screen:expect{grid=[[ @@ -2044,6 +2425,625 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| ]]} + + command('aunmenu PopUp | vmenu PopUp.Copy y') + + funcs.setreg('"', '') + meths.input_mouse('left', 'press', '2', 2, 1, 6) + screen:expect{grid=[[ + ## grid 1 + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {12:[No Name] [+] }| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] }{11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be {20:clicke}^d | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo | + {1:~ }| + ## grid 5 + some text | + to be {20:clicked} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + meths.input_mouse('right', 'press', '', 2, 1, 6) + meths.input_mouse('right', 'release', '', 2, 1, 6) + screen:expect{grid=[[ + ## grid 1 + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {12:[No Name] [+] }| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] }{11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be {20:clicke}^d | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo | + {1:~ }| + ## grid 5 + some text | + to be {20:clicked} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 6 + {21: Copy }| + ]], float_pos={ + [6] = {{id = -1}, "NW", 2, 2, 5, false, 250}; + }} + feed('<Down><CR>') + screen:expect{grid=[[ + ## grid 1 + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {12:[No Name] [+] }| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] }{11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be ^clicked | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 4 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo | + {1:~ }| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + eq('clicked', funcs.getreg('"')) + + funcs.setreg('"', '') + meths.input_mouse('left', 'press', '2', 4, 0, 64) + screen:expect{grid=[[ + ## grid 1 + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do {20:eiusm}^o | + {1:~ }| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + meths.input_mouse('right', 'press', '', 4, 0, 64) + meths.input_mouse('right', 'release', '', 4, 0, 64) + screen:expect{grid=[[ + ## grid 1 + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do {20:eiusm}^o | + {1:~ }| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 6 + {21: Copy }| + ]], float_pos={ + [6] = {{id = -1}, "NW", 4, 1, 63, false, 250}; + }} + feed('<Down><CR>') + screen:expect{grid=[[ + ## grid 1 + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 4 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ^eiusmo | + {1:~ }| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + eq('eiusmo', funcs.getreg('"')) + + command('wincmd J') + screen:try_resize_grid(4, 7, 10) + screen:expect{grid=[[ + ## grid 1 + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 4 + Lorem i| + psum do| + lor sit| + amet, | + consect| + etur ad| + ipiscin| + g elit,| + sed do| + ^eiusmo| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + + funcs.setreg('"', '') + meths.input_mouse('left', 'press', '2', 4, 9, 1) + screen:expect{grid=[[ + ## grid 1 + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + Lorem i| + psum do| + lor sit| + amet, | + consect| + etur ad| + ipiscin| + g elit,| + sed do| + {20:eiusm}^o| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + meths.input_mouse('right', 'press', '', 4, 9, 1) + meths.input_mouse('right', 'release', '', 4, 9, 1) + screen:expect{grid=[[ + ## grid 1 + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + Lorem i| + psum do| + lor sit| + amet, | + consect| + etur ad| + ipiscin| + g elit,| + sed do| + {20:eiusm}^o| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 6 + {21: Copy }| + ]], float_pos={ + [6] = {{id = -1}, "SW", 4, 9, 0, false, 250}; + }} + feed('<Down><CR>') + screen:expect{grid=[[ + ## grid 1 + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 4 + Lorem i| + psum do| + lor sit| + amet, | + consect| + etur ad| + ipiscin| + g elit,| + sed do| + ^eiusmo| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + eq('eiusmo', funcs.getreg('"')) + + screen:try_resize_grid(4, 7, 11) + screen:expect{grid=[[ + ## grid 1 + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 4 + ^Lorem i| + psum do| + lor sit| + amet, | + consect| + etur ad| + ipiscin| + g elit,| + sed do| + eiusmo| + {1:~ }| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + + funcs.setreg('"', '') + meths.input_mouse('left', 'press', '2', 4, 9, 1) + screen:expect{grid=[[ + ## grid 1 + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + Lorem i| + psum do| + lor sit| + amet, | + consect| + etur ad| + ipiscin| + g elit,| + sed do| + {20:eiusm}^o| + {1:~ }| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + meths.input_mouse('right', 'press', '', 4, 9, 1) + meths.input_mouse('right', 'release', '', 4, 9, 1) + screen:expect{grid=[[ + ## grid 1 + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + Lorem i| + psum do| + lor sit| + amet, | + consect| + etur ad| + ipiscin| + g elit,| + sed do| + {20:eiusm}^o| + {1:~ }| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 6 + {21: Copy }| + ]], float_pos={ + [6] = {{id = -1}, "NW", 4, 10, 0, false, 250}; + }} + feed('<Down><CR>') + screen:expect{grid=[[ + ## grid 1 + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + [5:------------------------------]│[2:----------------------]| + {12:[No Name] [+] [No Name] [+] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 4 + Lorem i| + psum do| + lor sit| + amet, | + consect| + etur ad| + ipiscin| + g elit,| + sed do| + ^eiusmo| + {1:~ }| + ## grid 5 + some text | + to be clicked | + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + eq('eiusmo', funcs.getreg('"')) end) it('supports mouse drag with mouse=a', function() diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 0b71e12b6f..76038472bd 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -5,6 +5,7 @@ local clear, feed = helpers.clear, helpers.feed local source = helpers.source local insert = helpers.insert local meths = helpers.meths +local async_meths = helpers.async_meths local command = helpers.command local funcs = helpers.funcs local eq = helpers.eq @@ -1978,6 +1979,54 @@ describe('builtin popupmenu', function() {2:-- }{5:match 1 of 4} | ]]) end + + feed('\n<c-x><c-n>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:-----------]│[2:--------------------]| + [4:-----------]│[2:--------------------]| + [4:-----------]│[2:--------------------]| + [4:-----------]│[2:--------------------]| + [4:-----------]│[2:--------------------]| + [4:-----------]│[2:--------------------]| + {3:<Name] [+] }{4:[No Name] [+] }| + [3:--------------------------------]| + ## grid 2 + aaa aab aac | + bbb aaa | + c aaabcdef ccc aaa | + aaa^ | + {1:~ }| + {1:~ }| + ## grid 3 + {2:-- }{5:match 1 of 6} | + ## grid 4 + aaa aab aac| + bbb aaa | + c aaabcdef | + ccc aaa | + aaa | + {1:~ }| + ## grid 5 + {s: aaa }{c: }| + {n: aab }{s: }| + {n: aac }{s: }| + ]], float_pos={ + [5] = {{id = -1}, "NW", 2, 4, -1, false, 100}; + }} + else + screen:expect([[ + aaa aab aac│aaa aab aac | + bbb aaa │bbb aaa | + c aaabcdef │c aaabcdef ccc aaa | + ccc aaa │aaa^ | + aaa {s: aaa }{c: }{1: }| + {1:~ }{n: aab }{s: }{1: }| + {3:<Name] [+] }{n: aac }{s: }{4: }| + {2:-- }{5:match 1 of 6} | + ]]) + end end) if not multigrid then @@ -2396,7 +2445,7 @@ describe('builtin popupmenu', function() -- can't draw the pum, but check we don't crash screen:try_resize(12,2) screen:expect([[ - text^ | + {1:<<<}t^ | {2:-- INSERT -} | ]]) @@ -2488,10 +2537,10 @@ describe('builtin popupmenu', function() funcs.complete(16, {'word', 'choice', 'text', 'thing'}) screen:expect([[ ^ tfelthgir emos| - {1: }{n: drow}{1: ~}| - {1: }{n: eciohc}{1: ~}| - {1: }{n: txet}{1: ~}| - {1: }{n: gniht}{1: ~}| + {1: }{n: drow }{1: ~}| + {1: }{n: eciohc }{1: ~}| + {1: }{n: txet }{1: ~}| + {1: }{n: gniht }{1: ~}| {1: ~}| {1: ~}| {1: ~}| @@ -2512,10 +2561,10 @@ describe('builtin popupmenu', function() feed('<c-n>') screen:expect([[ ^ drow tfelthgir emos| - {1: }{s: drow}{1: ~}| - {1: }{n: eciohc}{1: ~}| - {1: }{n: txet}{1: ~}| - {1: }{n: gniht}{1: ~}| + {1: }{s: drow }{1: ~}| + {1: }{n: eciohc }{1: ~}| + {1: }{n: txet }{1: ~}| + {1: }{n: gniht }{1: ~}| {1: ~}| {1: ~}| {1: ~}| @@ -2609,10 +2658,11 @@ describe('builtin popupmenu', function() end it('with rightleft vsplits', function() - screen:try_resize(40, 8) + screen:try_resize(40, 6) command('set rightleft') command('rightbelow vsplit') - command("set completeopt+=noinsert,noselect") + command('set completeopt+=noinsert,noselect') + command('set pumheight=2') feed('isome rightleft ') funcs.complete(16, {'word', 'choice', 'text', 'thing'}) if multigrid then @@ -2622,8 +2672,6 @@ describe('builtin popupmenu', function() [2:-------------------]│[4:--------------------]| [2:-------------------]│[4:--------------------]| [2:-------------------]│[4:--------------------]| - [2:-------------------]│[4:--------------------]| - [2:-------------------]│[4:--------------------]| {3:[No Name] [+] }{4:[No Name] [+] }| [3:----------------------------------------]| ## grid 2 @@ -2631,8 +2679,6 @@ describe('builtin popupmenu', function() {1: ~}| {1: ~}| {1: ~}| - {1: ~}| - {1: ~}| ## grid 3 {2:-- INSERT --} | ## grid 4 @@ -2640,28 +2686,134 @@ describe('builtin popupmenu', function() {1: ~}| {1: ~}| {1: ~}| - {1: ~}| - {1: ~}| ## grid 5 - {n: drow}| - {n: eciohc}| - {n: txet}| - {n: gniht}| + {c: }{n: drow }| + {s: }{n: eciohc }| ]], float_pos={ [5] = {{id = -1}, "NW", 4, 1, -11, false, 100}; }} else screen:expect([[ tfelthgir emos│ ^ tfelthgir emos| - {1: }{n: drow}{1: ~}| - {1: }{n: eciohc}{1: ~}| - {1: }{n: txet}{1: ~}| - {1: }{n: gniht}{1: ~}| + {1: }{c: }{n: drow }{1: ~}| + {1: }{s: }{n: eciohc }{1: ~}| {1: ~}│{1: ~}| {3:[No Name] [+] }{4:[No Name] [+] }| {2:-- INSERT --} | ]]) end + feed('<C-E><CR>') + funcs.complete(1, {'word', 'choice', 'text', 'thing'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + {3:[No Name] [+] }{4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + tfelthgir emos| + | + {1: ~}| + {1: ~}| + ## grid 3 + {2:-- INSERT --} | + ## grid 4 + tfelthgir emos| + ^ | + {1: ~}| + {1: ~}| + ## grid 5 + {c: }{n: drow}| + {s: }{n: eciohc}| + ]], float_pos={ + [5] = {{id = -1}, "NW", 4, 2, 4, false, 100}; + }} + else + screen:expect([[ + tfelthgir emos│ tfelthgir emos| + │ ^ | + {1: ~}│{1: }{c: }{n: drow}| + {1: ~}│{1: }{s: }{n: eciohc}| + {3:[No Name] [+] }{4:[No Name] [+] }| + {2:-- INSERT --} | + ]]) + end + feed('<C-E>') + async_meths.call_function('input', {'', '', 'sign'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + {3:[No Name] [+] }{4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + tfelthgir emos| + | + {1: ~}| + {1: ~}| + ## grid 3 + ^ | + ## grid 4 + tfelthgir emos| + | + {1: ~}| + {1: ~}| + ]]} + else + screen:expect([[ + tfelthgir emos│ tfelthgir emos| + │ | + {1: ~}│{1: ~}| + {1: ~}│{1: ~}| + {3:[No Name] [+] }{4:[No Name] [+] }| + ^ | + ]]) + end + command('set wildoptions+=pum') + feed('<Tab>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + [2:-------------------]│[4:--------------------]| + {3:[No Name] [+] }{4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + tfelthgir emos| + | + {1: ~}| + {1: ~}| + ## grid 3 + define^ | + ## grid 4 + tfelthgir emos| + | + {1: ~}| + {1: ~}| + ## grid 5 + {s:define }{c: }| + {n:jump }{s: }| + ]], float_pos={ + [5] = {{id = -1}, "SW", 1, 5, 0, false, 250}; + }} + else + screen:expect([[ + tfelthgir emos│ tfelthgir emos| + │ | + {1: ~}│{1: ~}| + {s:define }{c: }{1: ~}│{1: ~}| + {n:jump }{s: }{3: }{4:[No Name] [+] }| + define^ | + ]]) + end end) if not multigrid then @@ -4357,6 +4509,79 @@ describe('builtin popupmenu', function() ]]) end eq('foo', meths.get_var('menustr')) + + command('setlocal winbar=WINBAR') + if multigrid then + meths.input_mouse('right', 'press', '', 6, 1, 14) + screen:expect({grid=[[ + ## grid 1 + [2:--------------------------------]| + [2:--------------------------------]| + {3:[No Name] [+] }| + [5:---------------]│[6:----------------]| + [5:---------------]│[6:----------------]| + [3:--------------------------------]| + ## grid 2 + popup menu test | + {1:~ }| + ## grid 3 + :let g:menustr = 'foo' | + ## grid 4 + {n: foo}| + {n: bar}| + {n: baz}| + ## grid 5 + popup menu test| + {1:~ }| + ## grid 6 + {2:WINBAR }| + ^popup menu test | + ]], float_pos={[4] = {{id = -1}, "SW", 6, 1, 12, false, 250}}}) + else + feed('<RightMouse><30,4>') + screen:expect([[ + popup menu test | + {1:~ }{n: foo}| + {3:[No Name] [+] }{n: bar}| + popup menu test│{2:WINBAR }{n: baz}| + {1:~ }│^popup menu test | + :let g:menustr = 'foo' | + ]]) + end + if multigrid then + meths.input_mouse('left', 'press', '', 4, 1, 2) + screen:expect({grid=[[ + ## grid 1 + [2:--------------------------------]| + [2:--------------------------------]| + {3:[No Name] [+] }| + [5:---------------]│[6:----------------]| + [5:---------------]│[6:----------------]| + [3:--------------------------------]| + ## grid 2 + popup menu test | + {1:~ }| + ## grid 3 + :let g:menustr = 'bar' | + ## grid 5 + popup menu test| + {1:~ }| + ## grid 6 + {2:WINBAR }| + ^popup menu test | + ]]}) + else + feed('<LeftMouse><31,2>') + screen:expect([[ + popup menu test | + {1:~ }| + {3:[No Name] [+] }| + popup menu test│{2:WINBAR }| + {1:~ }│^popup menu test | + :let g:menustr = 'bar' | + ]]) + end + eq('bar', meths.get_var('menustr')) end) if not multigrid then diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua index 361f83d1ce..15819aef40 100644 --- a/test/functional/ui/spell_spec.lua +++ b/test/functional/ui/spell_spec.lua @@ -6,6 +6,8 @@ local clear = helpers.clear local feed = helpers.feed local insert = helpers.insert local command = helpers.command +local meths = helpers.meths +local curbufmeths = helpers.curbufmeths local is_os = helpers.is_os describe("'spell'", function() @@ -18,11 +20,13 @@ describe("'spell'", function() screen:set_default_attr_ids( { [0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {special = Screen.colors.Red, undercurl = true}, - [2] = {special = Screen.colors.Blue1, undercurl = true}, + [2] = {special = Screen.colors.Blue, undercurl = true}, [3] = {foreground = tonumber('0x6a0dad')}, [4] = {foreground = Screen.colors.Magenta}, [5] = {bold = true, foreground = Screen.colors.SeaGreen}, [6] = {foreground = Screen.colors.Red}, + [7] = {foreground = Screen.colors.Blue}, + [8] = {foreground = Screen.colors.Blue, special = Screen.colors.Red, undercurl = true}, }) end) @@ -74,93 +78,180 @@ describe("'spell'", function() ]]) end) - it('"noplainbuffer" and syntax #20385', function() + it('extmarks, "noplainbuffer" and syntax #20385 #23398', function() command('set filetype=c') command('syntax on') command('set spell') insert([[ #include <stdbool.h> - bool func(void);]]) + bool func(void); + // I am a speling mistakke]]) + feed('ge') screen:expect([[ {3:#include }{4:<stdbool.h>} | - {5:bool} func({5:void})^; | + {5:bool} func({5:void}); | + {7:// I am a }{8:spelin^g}{7: }{8:mistakke} | {0:~ }| {0:~ }| {0:~ }| {0:~ }| + | + ]]) + feed(']s') + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} func({5:void}); | + {7:// I am a }{8:speling}{7: }{8:^mistakke} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed(']s') + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} func({5:void}); | + {7:// I am a }{8:^speling}{7: }{8:mistakke} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {6:search hit BOTTOM, continuing at TOP} | + ]]) + command('echo ""') + local ns = meths.create_namespace("spell") + -- extmark with spell=true enables spell + local id = curbufmeths.set_extmark(ns, 1, 4, { end_row = 1, end_col = 10, spell = true }) + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} {1:func}({5:void}); | + {7:// I am a }{8:^speling}{7: }{8:mistakke} | + {0:~ }| + {0:~ }| + {0:~ }| {0:~ }| | ]]) feed('[s') screen:expect([[ {3:#include }{4:<stdbool.h>} | - {5:bool} func({5:void})^; | + {5:bool} {1:^func}({5:void}); | + {7:// I am a }{8:speling}{7: }{8:mistakke} | + {0:~ }| + {0:~ }| {0:~ }| {0:~ }| + | + ]]) + curbufmeths.del_extmark(ns, id) + -- extmark with spell=false disables spell + id = curbufmeths.set_extmark(ns, 2, 18, { end_row = 2, end_col = 26, spell = false }) + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} ^func({5:void}); | + {7:// I am a }{8:speling}{7: mistakke} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed('[s') + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} func({5:void}); | + {7:// I am a }{8:^speling}{7: mistakke} | + {0:~ }| {0:~ }| {0:~ }| {0:~ }| {6:search hit TOP, continuing at BOTTOM} | ]]) + command('echo ""') + curbufmeths.del_extmark(ns, id) + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} func({5:void}); | + {7:// I am a }{8:^speling}{7: }{8:mistakke} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed(']s') + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} func({5:void}); | + {7:// I am a }{8:speling}{7: }{8:^mistakke} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) -- "noplainbuffer" shouldn't change spellchecking behavior with syntax enabled command('set spelloptions+=noplainbuffer') screen:expect_unchanged() - feed(']s') + feed('[s') screen:expect([[ {3:#include }{4:<stdbool.h>} | - {5:bool} func({5:void})^; | - {0:~ }| + {5:bool} func({5:void}); | + {7:// I am a }{8:^speling}{7: }{8:mistakke} | {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {6:search hit BOTTOM, continuing at TOP} | + | ]]) -- no spellchecking with "noplainbuffer" and syntax disabled command('syntax off') screen:expect([[ #include <stdbool.h> | - bool func(void)^; | + bool func(void); | + // I am a ^speling mistakke | {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {6:search hit BOTTOM, continuing at TOP} | + | ]]) - feed('[s') + feed(']s') screen:expect([[ #include <stdbool.h> | - bool func(void)^; | + bool func(void); | + // I am a ^speling mistakke | {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {6:search hit TOP, continuing at BOTTOM} | + {6:search hit BOTTOM, continuing at TOP} | ]]) + command('echo ""') -- everything is spellchecked without "noplainbuffer" with syntax disabled command('set spelloptions&') screen:expect([[ #include <{1:stdbool}.h> | - {1:bool} {1:func}(void)^; | - {0:~ }| + {1:bool} {1:func}(void); | + // I am a {1:^speling} {1:mistakke} | {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {6:search hit TOP, continuing at BOTTOM} | + | ]]) - feed(']s') + feed('[s') screen:expect([[ - #include <{1:^stdbool}.h> | - {1:bool} {1:func}(void); | - {0:~ }| + #include <{1:stdbool}.h> | + {1:bool} {1:^func}(void); | + // I am a {1:speling} {1:mistakke} | {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {6:search hit BOTTOM, continuing at TOP} | + | ]]) end) + end) diff --git a/test/old/testdir/test_alot.vim b/test/old/testdir/test_alot.vim index 4a22315b9f..2a959f0834 100644 --- a/test/old/testdir/test_alot.vim +++ b/test/old/testdir/test_alot.vim @@ -17,7 +17,6 @@ source test_global.vim source test_move.vim source test_put.vim source test_reltime.vim -source test_scroll_opt.vim source test_searchpos.vim source test_set.vim source test_shift.vim diff --git a/test/old/testdir/test_breakindent.vim b/test/old/testdir/test_breakindent.vim index 0d1753182e..f6c0e32adf 100644 --- a/test/old/testdir/test_breakindent.vim +++ b/test/old/testdir/test_breakindent.vim @@ -87,7 +87,7 @@ func Test_breakindent02_vartabs() endif " simple breakindent test with showbreak set call s:test_windows('setl briopt=min:0 sbr=>> vts=4') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect = [ \ " abcd", \ " >>qr", @@ -100,7 +100,7 @@ endfunc func Test_breakindent03() " simple breakindent test with showbreak set and briopt including sbr call s:test_windows('setl briopt=sbr,min:0 sbr=++') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect=[ \ " abcd", \ "++ qrst", @@ -117,7 +117,7 @@ func Test_breakindent03_vartabs() return endif call s:test_windows('setl briopt=sbr,min:0 sbr=++ vts=4') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect = [ \ " abcd", \ "++ qrst", @@ -132,7 +132,7 @@ func Test_breakindent04() " breakindent set with min width 18 set sbr=<<< call s:test_windows('setl sbr=NONE briopt=min:18') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect = [ \ " abcd", \ " qrstuv", @@ -150,7 +150,7 @@ func Test_breakindent04_vartabs() return endif call s:test_windows('setl sbr= briopt=min:18 vts=4') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect = [ \ " abcd", \ " qrstuv", @@ -583,7 +583,7 @@ func Test_breakindent16() redraw! let lines = s:screen_lines(1,10) let expect = [ - \ " 789012", + \ "<<< 789012", \ " 345678", \ " 901234", \ ] @@ -611,7 +611,7 @@ func Test_breakindent16_vartabs() redraw! let lines = s:screen_lines(1,10) let expect = [ - \ " 789012", + \ "<<< 789012", \ " 345678", \ " 901234", \ ] @@ -711,25 +711,25 @@ endfunc func Test_breakindent20_cpo_n_nextpage() let s:input = "" call s:test_windows('setl breakindent briopt=min:14 cpo+=n number') - call setline(1, repeat('a', 200)) + call setline(1, repeat('abcdefghijklmnopqrst', 10)) norm! 1gg redraw! let lines = s:screen_lines(1, 20) let expect = [ - \ " 1 aaaaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaaaa", + \ " 1 abcdefghijklmnop", + \ " qrstabcdefghijkl", + \ " mnopqrstabcdefgh", \ ] call s:compare_lines(expect, lines) " Scroll down one screen line setl scrolloff=5 - norm! 5gj + norm! 6gj redraw! let lines = s:screen_lines(1, 20) let expect = [ - \ "--1 aaaaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaaaa", + \ "<<< qrstabcdefghijkl", + \ " mnopqrstabcdefgh", + \ " ijklmnopqrstabcd", \ ] call s:compare_lines(expect, lines) @@ -737,18 +737,18 @@ func Test_breakindent20_cpo_n_nextpage() norm! 1gg let lines = s:screen_lines(1, 20) let expect = [ - \ " 1 aaaaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaa", + \ " 1 abcdefghijklmnop", + \ " qrstabcdefghij", + \ " klmnopqrstabcd", \ ] call s:compare_lines(expect, lines) " Scroll down one screen line - norm! 5gj + norm! 6gj let lines = s:screen_lines(1, 20) let expect = [ - \ "--1 aaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaa", + \ "<<< qrstabcdefghij", + \ " klmnopqrstabcd", + \ " efghijklmnopqr", \ ] call s:compare_lines(expect, lines) diff --git a/test/old/testdir/test_conceal.vim b/test/old/testdir/test_conceal.vim index e3b8f767b8..63e17d8f2f 100644 --- a/test/old/testdir/test_conceal.vim +++ b/test/old/testdir/test_conceal.vim @@ -188,6 +188,32 @@ func Test_conceal_resize_term() call StopVimInTerminal(buf) endfunc +func Test_conceal_linebreak() + CheckScreendump + + let code =<< trim [CODE] + vim9script + &wrap = true + &conceallevel = 2 + &concealcursor = 'nc' + &linebreak = true + &showbreak = '+ ' + var line: string = 'a`a`a`a`' + .. 'a'->repeat(&columns - 15) + .. ' b`b`' + .. 'b'->repeat(&columns - 10) + .. ' cccccc' + ['x'->repeat(&columns), '', line]->setline(1) + syntax region CodeSpan matchgroup=Delimiter start=/\z(`\+\)/ end=/\z1/ concealends + [CODE] + call writefile(code, 'XTest_conceal_linebreak', 'D') + let buf = RunVimInTerminal('-S XTest_conceal_linebreak', {'rows': 8}) + call VerifyScreenDump(buf, 'Test_conceal_linebreak_1', {}) + + " clean up + call StopVimInTerminal(buf) +endfunc + " Tests for correct display (cursor column position) with +conceal and " tabulators. Need to run this test in a separate Vim instance. Otherwise the " screen is not updated (lazy redraw) and the cursor position is wrong. diff --git a/test/old/testdir/test_diffmode.vim b/test/old/testdir/test_diffmode.vim index 0049398776..ac90aaaa02 100644 --- a/test/old/testdir/test_diffmode.vim +++ b/test/old/testdir/test_diffmode.vim @@ -1605,6 +1605,21 @@ func Test_diff_scroll() call delete('Xright') endfunc +" This was scrolling too many lines. +func Test_diff_scroll_wrap_on() + 20new + 40vsplit + call setline(1, map(range(1, 9), 'repeat(v:val, 200)')) + setlocal number diff so=0 + redraw + normal! jj + call assert_equal(1, winsaveview().topline) + normal! j + call assert_equal(2, winsaveview().topline) + bwipe! + bwipe! +endfunc + " This was trying to update diffs for a buffer being closed func Test_diff_only() silent! lfile diff --git a/test/old/testdir/test_display.vim b/test/old/testdir/test_display.vim index b642f39c9f..f27a8362a9 100644 --- a/test/old/testdir/test_display.vim +++ b/test/old/testdir/test_display.vim @@ -478,5 +478,26 @@ func Test_display_lastline() call assert_fails(':set fillchars=lastline:〇', 'E474:') endfunc +func Test_display_long_lastline() + CheckScreendump + + let lines =<< trim END + set display=lastline + call setline(1, [ + \'aaaaa'->repeat(100), + \'bbbbb '->repeat(7) .. 'ccccc '->repeat(7) .. 'ddddd '->repeat(7) + \]) + END + + call writefile(lines, 'XdispLongline', 'D') + let buf = RunVimInTerminal('-S XdispLongline', #{rows: 14, cols: 35}) + + call term_sendkeys(buf, "482|") + call VerifyScreenDump(buf, 'Test_display_long_line_1', {}) + call term_sendkeys(buf, "D") + call VerifyScreenDump(buf, 'Test_display_long_line_2', {}) + + call StopVimInTerminal(buf) +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index 789430fc84..2d7a24090f 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -346,6 +346,7 @@ let s:filename_checks = { \ 'lsl': ['file.lsl'], \ 'lss': ['file.lss'], \ 'lua': ['file.lua', 'file.rockspec', 'file.nse', '.luacheckrc', '.busted'], + \ 'luau': ['file.luau'], \ 'lynx': ['lynx.cfg'], \ 'lyrics': ['file.lrc'], \ 'm3build': ['m3makefile', 'm3overrides'], diff --git a/test/old/testdir/test_let.vim b/test/old/testdir/test_let.vim index 0d84164274..bf119bdeab 100644 --- a/test/old/testdir/test_let.vim +++ b/test/old/testdir/test_let.vim @@ -338,7 +338,43 @@ func Test_let_heredoc_fails() call assert_report('No exception thrown') catch /E488:/ catch - call assert_report("Caught exception: " .. v:exception) + call assert_report('Caught exception: ' .. v:exception) + endtry + + try + let &commentstring =<< trim TEXT + change + insert + append + TEXT + call assert_report('No exception thrown') + catch /E730:/ + catch + call assert_report('Caught exception: ' .. v:exception) + endtry + + try + let $SOME_ENV_VAR =<< trim TEXT + change + insert + append + TEXT + call assert_report('No exception thrown') + catch /E730:/ + catch + call assert_report('Caught exception: ' .. v:exception) + endtry + + try + let @r =<< trim TEXT + change + insert + append + TEXT + call assert_report('No exception thrown') + catch /E730:/ + catch + call assert_report('Caught exception: ' .. v:exception) endtry let text =<< trim END @@ -504,6 +540,32 @@ E z END call assert_equal([' x', ' \y', ' z'], [a, b, c]) + + " unpack assignment without whitespace + let[a,b,c]=<<END +change +insert +append +END + call assert_equal(['change', 'insert', 'append'], [a, b, c]) + + " curly braces name and list slice assignment + let foo_3_bar = ['', '', ''] + let foo_{1 + 2}_bar[ : ] =<< END +change +insert +append +END + call assert_equal(['change', 'insert', 'append'], foo_3_bar) + + " dictionary key containing brackets and spaces + let d = {'abc] 123': 'baz'} + let d[d['abc] 123'] .. '{'] =<< END +change +insert +append +END + call assert_equal(['change', 'insert', 'append'], d['baz{']) endfunc " Test for evaluating Vim expressions in a heredoc using {expr} diff --git a/test/old/testdir/test_listlbr.vim b/test/old/testdir/test_listlbr.vim index a746779e73..2e66fd4ccb 100644 --- a/test/old/testdir/test_listlbr.vim +++ b/test/old/testdir/test_listlbr.vim @@ -223,7 +223,7 @@ func Test_virtual_block_and_vbA() exe "norm! $3B\<C-v>eAx\<Esc>" let lines = s:screen_lines([1, 10], winwidth(0)) let expect = [ -\ "foobar foobar ", +\ "<<<bar foobar ", \ "foobar foobar ", \ "foobar foobar ", \ "foobar foobar ", diff --git a/test/old/testdir/test_listlbr_utf8.vim b/test/old/testdir/test_listlbr_utf8.vim index df1ed78119..15b248964f 100644 --- a/test/old/testdir/test_listlbr_utf8.vim +++ b/test/old/testdir/test_listlbr_utf8.vim @@ -266,7 +266,7 @@ func Test_chinese_char_on_wrap_column() norm! $ redraw! let expect=[ -\ '中aaaaaaaaaaaaaaaaa>', +\ '<<<aaaaaaaaaaaaaaaa>', \ '中aaaaaaaaaaaaaaaaa>', \ '中aaaaaaaaaaaaaaaaa>', \ '中aaaaaaaaaaaaaaaaa>', diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim index fe8611d527..330a16dffb 100644 --- a/test/old/testdir/test_normal.vim +++ b/test/old/testdir/test_normal.vim @@ -250,9 +250,10 @@ func Test_normal_formatexpr_returns_nonzero() setlocal formatexpr=Format() normal VGgq call assert_equal(['one two'], getline(1, '$')) + setlocal formatexpr= delfunc Format - close! + bwipe! endfunc " Test for using a script-local function for 'formatexpr' @@ -1329,7 +1330,7 @@ func Test_vert_scroll_cmds() call assert_equal(15, line('w$')) set foldenable& - close! + bwipe! endfunc func Test_scroll_in_ex_mode() @@ -2350,7 +2351,7 @@ func Test_normal_section() call assert_equal(2, line('.')) call assert_equal(-1, foldclosedend(line('.'))) - close! + bwipe! endfunc " Test for changing case using u, U, gu, gU and ~ (tilde) commands @@ -2447,7 +2448,8 @@ func Test_normal_changecase_turkish() " can't use Turkish locale throw 'Skipped: Turkish locale not available' endtry - close! + + bwipe! endfunc " Test for r (replace) command @@ -2524,7 +2526,6 @@ endfunc " Test for g`, g;, g,, g&, gv, gk, gj, gJ, g0, g^, g_, gm, g$, gM, g CTRL-G, " gi and gI commands func Test_normal33_g_cmd2() - CheckFeature jumplist call Setup_NewWindow() " Test for g` clearjumps @@ -2982,7 +2983,8 @@ func Test_normal_nvend() call assert_equal([4, 5], [line('.'), col('.')]) exe "normal! \<C-End>" call assert_equal([10, 6], [line('.'), col('.')]) - close! + + bwipe! endfunc " Test for cw cW ce @@ -3479,12 +3481,11 @@ func Test_java_motion() call assert_equal([7, 8, 15], [line('.'), col('.'), virtcol('.')]) call assert_equal(-1, foldclosedend(7)) - close! + bwipe! endfunc " Tests for g cmds func Test_normal_gdollar_cmd() - CheckFeature jumplist call Setup_NewWindow() " Make long lines that will wrap %s/$/\=repeat(' foobar', 10)/ @@ -3595,7 +3596,8 @@ func Test_normal_yank_with_excmd() let @a = '' call feedkeys("\"ay:if v:true\<CR>normal l\<CR>endif\<CR>", 'xt') call assert_equal('f', @a) - close! + + bwipe! endfunc " Test for supplying a count to a normal-mode command across a cursorhold call @@ -3617,7 +3619,8 @@ func Test_normal_cursorhold_with_count() au! augroup END au! normalcHoldTest - close! + + bwipe! delfunc s:cHold endfunc @@ -3641,7 +3644,8 @@ func Test_horiz_motion() call assert_equal(11, col('.')) exe "normal! $\<C-BS>" call assert_equal(10, col('.')) - close! + + bwipe! endfunc " Test for using a : command in operator pending mode @@ -3649,7 +3653,7 @@ func Test_normal_colon_op() new call setline(1, ['one', 'two']) call assert_beeps("normal! Gc:d\<CR>") - close! + bwipe! endfunc " Test for d and D commands @@ -3674,7 +3678,7 @@ func Test_normal_delete_cmd() call assert_fails('normal D', 'E21:') call assert_fails('normal d$', 'E21:') - close! + bwipe! endfunc " Test for deleting or changing characters across lines with 'whichwrap' @@ -3694,7 +3698,8 @@ func Test_normal_op_across_lines() call setline(1, ['one two', 'three four']) exe "norm! $3x" call assert_equal(['one twhree four'], getline(1, '$')) - close! + + bwipe! set whichwrap& endfunc @@ -3732,23 +3737,54 @@ func Test_normal_word_move() normal 3Gyb call assert_equal("two\n ", @") - close! + bwipe! endfunc " Test for 'scrolloff' with a long line that doesn't fit in the screen -func Test_normal_scroloff() +func Test_normal_scrolloff() 10new - 80vnew - call setline(1, repeat('a', 1000)) + 60vnew + call setline(1, ' 1 ' .. repeat('a', 57) + \ .. ' 2 ' .. repeat('b', 57) + \ .. ' 3 ' .. repeat('c', 57) + \ .. ' 4 ' .. repeat('d', 57) + \ .. ' 5 ' .. repeat('e', 57) + \ .. ' 6 ' .. repeat('f', 57) + \ .. ' 7 ' .. repeat('g', 57) + \ .. ' 8 ' .. repeat('h', 57) + \ .. ' 9 ' .. repeat('i', 57) + \ .. '10 ' .. repeat('j', 57) + \ .. '11 ' .. repeat('k', 57) + \ .. '12 ' .. repeat('l', 57) + \ .. '13 ' .. repeat('m', 57) + \ .. '14 ' .. repeat('n', 57) + \ .. '15 ' .. repeat('o', 57) + \ .. '16 ' .. repeat('p', 57) + \ .. '17 ' .. repeat('q', 57) + \ .. '18 ' .. repeat('r', 57) + \ .. '19 ' .. repeat('s', 57) + \ .. '20 ' .. repeat('t', 57) + \ .. '21 ' .. repeat('u', 57) + \ .. '22 ' .. repeat('v', 57) + \ .. '23 ' .. repeat('w', 57) + \ .. '24 ' .. repeat('x', 57) + \ .. '25 ' .. repeat('y', 57) + \ .. '26 ' .. repeat('z', 57) + \ ) set scrolloff=10 normal gg10gj - call assert_equal(8, winline()) + call assert_equal(6, winline()) normal 10gj - call assert_equal(10, winline()) + call assert_equal(6, winline()) normal 10gk - call assert_equal(3, winline()) + call assert_equal(6, winline()) + normal 0 + call assert_equal(1, winline()) + normal $ + call assert_equal(10, winline()) + set scrolloff& - close! + bwipe! endfunc " Test for vertical scrolling with CTRL-F and CTRL-B with a long line @@ -3768,7 +3804,8 @@ func Test_normal_vert_scroll_longline() exe "normal \<C-B>\<C-B>" call assert_equal(5, line('.')) call assert_equal(5, winline()) - close! + + bwipe! endfunc " Test for jumping in a file using % @@ -3781,7 +3818,8 @@ func Test_normal_percent_jump() call feedkeys('50%', 'xt') call assert_equal(50, line('.')) call assert_equal(-1, foldclosedend(50)) - close! + + bwipe! endfunc " Test for << and >> commands to shift text by 'shiftwidth' @@ -3874,24 +3912,25 @@ func Test_mouse_shape_after_failed_change() CheckCanRunGui let lines =<< trim END + vim9script set mouseshape+=o:busy setlocal nomodifiable - let g:mouse_shapes = [] - - func SaveMouseShape(timer) - let g:mouse_shapes += [getmouseshape()] - endfunc - - func SaveAndQuit(timer) - call writefile(g:mouse_shapes, 'Xmouseshapes') - quit - endfunc + var mouse_shapes = [] - call timer_start(50, {_ -> feedkeys('c')}) - call timer_start(100, 'SaveMouseShape') - call timer_start(150, {_ -> feedkeys('c')}) - call timer_start(200, 'SaveMouseShape') - call timer_start(250, 'SaveAndQuit') + feedkeys('c') + timer_start(50, (_) => { + mouse_shapes += [getmouseshape()] + timer_start(50, (_) => { + feedkeys('c') + timer_start(50, (_) => { + mouse_shapes += [getmouseshape()] + timer_start(50, (_) => { + writefile(mouse_shapes, 'Xmouseshapes') + quit + }) + }) + }) + }) END call writefile(lines, 'Xmouseshape.vim', 'D') call RunVim([], [], "-g -S Xmouseshape.vim") diff --git a/test/old/testdir/test_number.vim b/test/old/testdir/test_number.vim index 521b0cf706..cf777fd918 100644 --- a/test/old/testdir/test_number.vim +++ b/test/old/testdir/test_number.vim @@ -138,7 +138,7 @@ func Test_number_with_linewrap1() call s:validate_cursor() let lines = s:screen_lines(1, 3) let expect = [ -\ "--1 aaaa", +\ "<<< aaaa", \ " aaaa", \ " aaaa", \ ] diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim index f101f550d1..8fc86a99e3 100644 --- a/test/old/testdir/test_options.vim +++ b/test/old/testdir/test_options.vim @@ -721,7 +721,7 @@ func Test_backupskip() let &backupskip = backupskip endfunc -func Test_copy_winopt() +func Test_buf_copy_winopt() set hidden " Test copy option from current buffer in window @@ -775,6 +775,108 @@ func Test_copy_winopt() set hidden& endfunc +func Test_split_copy_options() + let values = [ + \['cursorbind', 1, 0], + \['fillchars', '"vert:-"', '"' .. &fillchars .. '"'], + \['list', 1, 0], + \['listchars', '"space:-"', '"' .. &listchars .. '"'], + \['number', 1, 0], + \['relativenumber', 1, 0], + \['scrollbind', 1, 0], + \['smoothscroll', 1, 0], + \['virtualedit', '"block"', '"' .. &virtualedit .. '"'], + "\ ['wincolor', '"Search"', '"' .. &wincolor .. '"'], + \['wrap', 0, 1], + \] + if has('linebreak') + let values += [ + \['breakindent', 1, 0], + \['breakindentopt', '"min:5"', '"' .. &breakindentopt .. '"'], + \['linebreak', 1, 0], + \['numberwidth', 7, 4], + \['showbreak', '"++"', '"' .. &showbreak .. '"'], + \] + endif + if has('rightleft') + let values += [ + \['rightleft', 1, 0], + \['rightleftcmd', '"search"', '"' .. &rightleftcmd .. '"'], + \] + endif + if has('statusline') + let values += [ + \['statusline', '"---%f---"', '"' .. &statusline .. '"'], + \] + endif + if has('spell') + let values += [ + \['spell', 1, 0], + \] + endif + if has('syntax') + let values += [ + \['cursorcolumn', 1, 0], + \['cursorline', 1, 0], + \['cursorlineopt', '"screenline"', '"' .. &cursorlineopt .. '"'], + \['colorcolumn', '"+1"', '"' .. &colorcolumn .. '"'], + \] + endif + if has('diff') + let values += [ + \['diff', 1, 0], + \] + endif + if has('conceal') + let values += [ + \['concealcursor', '"nv"', '"' .. &concealcursor .. '"'], + \['conceallevel', '3', &conceallevel], + \] + endif + if has('terminal') + let values += [ + \['termwinkey', '"<C-X>"', '"' .. &termwinkey .. '"'], + \['termwinsize', '"10x20"', '"' .. &termwinsize .. '"'], + \] + endif + if has('folding') + let values += [ + \['foldcolumn', '"5"', &foldcolumn], + \['foldenable', 0, 1], + \['foldexpr', '"2 + 3"', '"' .. &foldexpr .. '"'], + \['foldignore', '"+="', '"' .. &foldignore .. '"'], + \['foldlevel', 4, &foldlevel], + \['foldmarker', '">>,<<"', '"' .. &foldmarker .. '"'], + \['foldmethod', '"marker"', '"' .. &foldmethod .. '"'], + \['foldminlines', 3, &foldminlines], + \['foldnestmax', 17, &foldnestmax], + \['foldtext', '"closed"', '"' .. &foldtext .. '"'], + \] + endif + if has('signs') + let values += [ + \['signcolumn', '"number"', '"' .. &signcolumn .. '"'], + \] + endif + + " set options to non-default value + for item in values + exe $"let &{item[0]} = {item[1]}" + endfor + + " check values are set in new window + split + for item in values + exe $'call assert_equal({item[1]}, &{item[0]}, "{item[0]}")' + endfor + + " restore + close + for item in values + exe $"let &{item[0]} = {item[1]}" + endfor +endfunc + func Test_shortmess_F() new call assert_match('\[No Name\]', execute('file')) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 64f4ced470..8402fa51e2 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -1,4 +1,8 @@ -" Test for reset 'scroll' +" Test for reset 'scroll' and 'smoothscroll' + +source check.vim +source screendump.vim +source mouse.vim func Test_reset_scroll() let scr = &l:scroll @@ -51,4 +55,563 @@ func Test_scolloff_even_line_count() bwipe! endfunc +func Test_CtrlE_CtrlY_stop_at_end() + enew + call setline(1, ['one', 'two']) + set number + exe "normal \<C-Y>" + call assert_equal([" 1 one "], ScreenLines(1, 10)) + exe "normal \<C-E>\<C-E>\<C-E>" + call assert_equal([" 2 two "], ScreenLines(1, 10)) + + bwipe! + set nonumber +endfunc + +func Test_smoothscroll_CtrlE_CtrlY() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, [ + 'line one', + 'word '->repeat(20), + 'line three', + 'long word '->repeat(7), + 'line', + 'line', + 'line', + ]) + set smoothscroll + :5 + END + call writefile(lines, 'XSmoothScroll', 'D') + let buf = RunVimInTerminal('-S XSmoothScroll', #{rows: 12, cols: 40}) + + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smoothscroll_1', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smoothscroll_2', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smoothscroll_3', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smoothscroll_4', {}) + + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smoothscroll_5', {}) + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smoothscroll_6', {}) + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smoothscroll_7', {}) + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smoothscroll_8', {}) + + if has('folding') + call term_sendkeys(buf, ":set foldmethod=indent\<CR>") + " move the cursor so we can reuse the same dumps + call term_sendkeys(buf, "5G") + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smoothscroll_1', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smoothscroll_2', {}) + call term_sendkeys(buf, "7G") + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smoothscroll_7', {}) + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smoothscroll_8', {}) + endif + + call StopVimInTerminal(buf) +endfunc + +func Test_smoothscroll_number() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, [ + 'one ' .. 'word '->repeat(20), + 'two ' .. 'long word '->repeat(7), + 'line', + 'line', + 'line', + ]) + set smoothscroll + set splitkeep=topline + set number cpo+=n + :3 + + def g:DoRel() + set number relativenumber scrolloff=0 + :%del + setline(1, [ + 'one', + 'very long text '->repeat(12), + 'three', + ]) + exe "normal 2Gzt\<C-E>" + enddef + END + call writefile(lines, 'XSmoothNumber', 'D') + let buf = RunVimInTerminal('-S XSmoothNumber', #{rows: 12, cols: 40}) + + call VerifyScreenDump(buf, 'Test_smooth_number_1', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_number_2', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_number_3', {}) + + call term_sendkeys(buf, ":set cpo-=n\<CR>") + call VerifyScreenDump(buf, 'Test_smooth_number_4', {}) + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smooth_number_5', {}) + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smooth_number_6', {}) + + call term_sendkeys(buf, ":botright split\<CR>gg") + call VerifyScreenDump(buf, 'Test_smooth_number_7', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_number_8', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_number_9', {}) + call term_sendkeys(buf, ":close\<CR>") + + call term_sendkeys(buf, ":call DoRel()\<CR>") + call VerifyScreenDump(buf, 'Test_smooth_number_10', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_smoothscroll_list() + CheckScreendump + + let lines =<< trim END + vim9script + set smoothscroll scrolloff=0 + set list + setline(1, [ + 'one', + 'very long text '->repeat(12), + 'three', + ]) + exe "normal 2Gzt\<C-E>" + END + call writefile(lines, 'XSmoothList', 'D') + let buf = RunVimInTerminal('-S XSmoothList', #{rows: 8, cols: 40}) + + call VerifyScreenDump(buf, 'Test_smooth_list_1', {}) + + call term_sendkeys(buf, ":set listchars+=precedes:#\<CR>") + call VerifyScreenDump(buf, 'Test_smooth_list_2', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_smoothscroll_diff_mode() + CheckScreendump + + let lines =<< trim END + vim9script + var text = 'just some text here' + setline(1, text) + set smoothscroll + diffthis + new + setline(1, text) + set smoothscroll + diffthis + END + call writefile(lines, 'XSmoothDiff', 'D') + let buf = RunVimInTerminal('-S XSmoothDiff', #{rows: 8}) + + call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_smoothscroll_wrap_scrolloff_zero() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) + set smoothscroll scrolloff=0 + :3 + END + call writefile(lines, 'XSmoothWrap', 'D') + let buf = RunVimInTerminal('-S XSmoothWrap', #{rows: 8, cols: 40}) + + call VerifyScreenDump(buf, 'Test_smooth_wrap_1', {}) + + " moving cursor down - whole bottom line shows + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_smooth_wrap_2', {}) + + call term_sendkeys(buf, "\<C-E>j") + call VerifyScreenDump(buf, 'Test_smooth_wrap_3', {}) + + call term_sendkeys(buf, "G") + call VerifyScreenDump(buf, 'Test_smooth_wrap_4', {}) + + " moving cursor up right after the >>> marker - no need to show whole line + call term_sendkeys(buf, "2gj3l2k") + call VerifyScreenDump(buf, 'Test_smooth_wrap_5', {}) + + " moving cursor up where the >>> marker is - whole top line shows + call term_sendkeys(buf, "2j02k") + call VerifyScreenDump(buf, 'Test_smooth_wrap_6', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_smoothscroll_wrap_long_line() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four']) + set smoothscroll scrolloff=0 + normal 3G10|zt + END + call writefile(lines, 'XSmoothWrap', 'D') + let buf = RunVimInTerminal('-S XSmoothWrap', #{rows: 6, cols: 40}) + call VerifyScreenDump(buf, 'Test_smooth_long_1', {}) + + " scrolling up, cursor moves screen line down + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_long_2', {}) + call term_sendkeys(buf, "5\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_long_3', {}) + + " scrolling down, cursor moves screen line up + call term_sendkeys(buf, "5\<C-Y>") + call VerifyScreenDump(buf, 'Test_smooth_long_4', {}) + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smooth_long_5', {}) + + " 'scrolloff' set to 1, scrolling up, cursor moves screen line down + call term_sendkeys(buf, ":set scrolloff=1\<CR>") + call term_sendkeys(buf, "10|\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_long_6', {}) + + " 'scrolloff' set to 1, scrolling down, cursor moves screen line up + call term_sendkeys(buf, "\<C-E>") + call term_sendkeys(buf, "gjgj") + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smooth_long_7', {}) + + " 'scrolloff' set to 2, scrolling up, cursor moves screen line down + call term_sendkeys(buf, ":set scrolloff=2\<CR>") + call term_sendkeys(buf, "10|\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_long_8', {}) + + " 'scrolloff' set to 2, scrolling down, cursor moves screen line up + call term_sendkeys(buf, "\<C-E>") + call term_sendkeys(buf, "gj") + call term_sendkeys(buf, "\<C-Y>") + call VerifyScreenDump(buf, 'Test_smooth_long_9', {}) + + " 'scrolloff' set to 0, move cursor down one line. + " Cursor should move properly, and since this is a really long line, it will + " be put on top of the screen. + call term_sendkeys(buf, ":set scrolloff=0\<CR>") + call term_sendkeys(buf, "0j") + call VerifyScreenDump(buf, 'Test_smooth_long_10', {}) + + " Test zt/zz/zb that they work properly when a long line is above it + call term_sendkeys(buf, "zb") + call VerifyScreenDump(buf, 'Test_smooth_long_11', {}) + call term_sendkeys(buf, "zz") + call VerifyScreenDump(buf, 'Test_smooth_long_12', {}) + call term_sendkeys(buf, "zt") + call VerifyScreenDump(buf, 'Test_smooth_long_13', {}) + + " Repeat the step and move the cursor down again. + " This time, use a shorter long line that is barely long enough to span more + " than one window. Note that the cursor is at the bottom this time because + " Vim prefers to do so if we are scrolling a few lines only. + call term_sendkeys(buf, ":call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])\<CR>") + call term_sendkeys(buf, "3Gzt") + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_smooth_long_14', {}) + + " Repeat the step but this time start it when the line is smooth-scrolled by + " one line. This tests that the offset calculation is still correct and + " still end up scrolling down to the next line with cursor at bottom of + " screen. + call term_sendkeys(buf, "3Gzt") + call term_sendkeys(buf, "\<C-E>j") + call VerifyScreenDump(buf, 'Test_smooth_long_15', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_smoothscroll_one_long_line() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, 'with lots of text '->repeat(7)) + set smoothscroll scrolloff=0 + END + call writefile(lines, 'XSmoothOneLong', 'D') + let buf = RunVimInTerminal('-S XSmoothOneLong', #{rows: 6, cols: 40}) + call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {}) + + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_one_long_2', {}) + + call term_sendkeys(buf, "0") + call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_smoothscroll_long_line_showbreak() + CheckScreendump + + let lines =<< trim END + vim9script + # a line that spans four screen lines + setline(1, 'with lots of text in one line '->repeat(6)) + set smoothscroll scrolloff=0 showbreak=+++\ + END + call writefile(lines, 'XSmoothLongShowbreak', 'D') + let buf = RunVimInTerminal('-S XSmoothLongShowbreak', #{rows: 6, cols: 40}) + call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {}) + + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_2', {}) + + call term_sendkeys(buf, "0") + call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {}) + + call StopVimInTerminal(buf) +endfunc + +func s:check_col_calc(win_col, win_line, buf_col) + call assert_equal(a:win_col, wincol()) + call assert_equal(a:win_line, winline()) + call assert_equal(a:buf_col, col('.')) +endfunc + +" Test that if the current cursor is on a smooth scrolled line, we correctly +" reposition it. Also check that we don't miscalculate the values by checking +" the consistency between wincol() and col('.') as they are calculated +" separately in code. +func Test_smoothscroll_cursor_position() + call NewWindow(10, 20) + setl smoothscroll wrap + call setline(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + + call s:check_col_calc(1, 1, 1) + exe "normal \<C-E>" + + " Move down another line to avoid blocking the <<< display + call s:check_col_calc(1, 2, 41) + exe "normal \<C-Y>" + call s:check_col_calc(1, 3, 41) + + normal gg3l + exe "normal \<C-E>" + + " Move down only 1 line when we are out of the range of the <<< display + call s:check_col_calc(4, 1, 24) + exe "normal \<C-Y>" + call s:check_col_calc(4, 2, 24) + normal ggg$ + exe "normal \<C-E>" + call s:check_col_calc(20, 1, 40) + exe "normal \<C-Y>" + call s:check_col_calc(20, 2, 40) + normal gg + + " Test number, where we have indented lines + setl number + call s:check_col_calc(5, 1, 1) + exe "normal \<C-E>" + + " Move down only 1 line when the <<< display is on the number column + call s:check_col_calc(5, 1, 17) + exe "normal \<C-Y>" + call s:check_col_calc(5, 2, 17) + normal ggg$ + exe "normal \<C-E>" + call s:check_col_calc(20, 1, 32) + exe "normal \<C-Y>" + call s:check_col_calc(20, 2, 32) + normal gg + + setl numberwidth=1 + + " Move down another line when numberwidth is too short to cover the whole + " <<< display + call s:check_col_calc(3, 1, 1) + exe "normal \<C-E>" + call s:check_col_calc(3, 2, 37) + exe "normal \<C-Y>" + call s:check_col_calc(3, 3, 37) + normal ggl + + " Only move 1 line down when we are just past the <<< display + call s:check_col_calc(4, 1, 2) + exe "normal \<C-E>" + call s:check_col_calc(4, 1, 20) + exe "normal \<C-Y>" + call s:check_col_calc(4, 2, 20) + normal gg + setl numberwidth& + + " Test number + showbreak, so test that the additional indentation works + setl number showbreak=+++ + call s:check_col_calc(5, 1, 1) + exe "normal \<C-E>" + call s:check_col_calc(8, 1, 17) + exe "normal \<C-Y>" + call s:check_col_calc(8, 2, 17) + normal gg + + " Test number + cpo+=n mode, where wrapped lines aren't indented + setl number cpo+=n showbreak= + call s:check_col_calc(5, 1, 1) + exe "normal \<C-E>" + call s:check_col_calc(1, 2, 37) + exe "normal \<C-Y>" + call s:check_col_calc(1, 3, 37) + normal gg + + bwipe! +endfunc + +func Test_smoothscroll_cursor_scrolloff() + call NewWindow(10, 20) + setl smoothscroll wrap + setl scrolloff=3 + + " 120 chars are 6 screen lines + call setline(1, "abcdefghijklmnopqrstABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstABCDEFGHIJKLMNOPQRST") + call setline(2, "below") + + call s:check_col_calc(1, 1, 1) + + " CTRL-E shows "<<<DEFG...", cursor move four lines down + exe "normal \<C-E>" + call s:check_col_calc(1, 4, 81) + + " cursor on start of second line, "gk" moves into first line, skipcol doesn't + " change + exe "normal G0gk" + call s:check_col_calc(1, 5, 101) + + " move cursor left one window width worth, scrolls one screen line + exe "normal 20h" + call s:check_col_calc(1, 5, 81) + + " move cursor left one window width worth, scrolls one screen line + exe "normal 20h" + call s:check_col_calc(1, 4, 61) + + " cursor on last line, "gk" should not cause a scroll + set scrolloff=0 + normal G0 + call s:check_col_calc(1, 7, 1) + normal gk + call s:check_col_calc(1, 6, 101) + + bwipe! +endfunc + + +" Test that mouse picking is still accurate when we have smooth scrolled lines +func Test_smoothscroll_mouse_pos() + CheckNotGui + CheckUnix + + let save_mouse = &mouse + "let save_term = &term + "let save_ttymouse = &ttymouse + set mouse=a "term=xterm ttymouse=xterm2 + + call NewWindow(10, 20) + setl smoothscroll wrap + " First line will wrap to 3 physical lines. 2nd/3rd lines are short lines. + call setline(1, ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "line 2", "line 3"]) + + func s:check_mouse_click(row, col, buf_row, buf_col) + call MouseLeftClick(a:row, a:col) + + call assert_equal(a:col, wincol()) + call assert_equal(a:row, winline()) + call assert_equal(a:buf_row, line('.')) + call assert_equal(a:buf_col, col('.')) + endfunc + + " Check that clicking without scroll works first. + call s:check_mouse_click(3, 5, 1, 45) + call s:check_mouse_click(4, 1, 2, 1) + call s:check_mouse_click(4, 6, 2, 6) + call s:check_mouse_click(5, 1, 3, 1) + call s:check_mouse_click(5, 6, 3, 6) + + " Smooth scroll, and checks that this didn't mess up mouse clicking + exe "normal \<C-E>" + call s:check_mouse_click(2, 5, 1, 45) + call s:check_mouse_click(3, 1, 2, 1) + call s:check_mouse_click(3, 6, 2, 6) + call s:check_mouse_click(4, 1, 3, 1) + call s:check_mouse_click(4, 6, 3, 6) + + exe "normal \<C-E>" + call s:check_mouse_click(1, 5, 1, 45) + call s:check_mouse_click(2, 1, 2, 1) + call s:check_mouse_click(2, 6, 2, 6) + call s:check_mouse_click(3, 1, 3, 1) + call s:check_mouse_click(3, 6, 3, 6) + + " Make a new first line 11 physical lines tall so it's taller than window + " height, to test overflow calculations with really long lines wrapping. + normal gg + call setline(1, "12345678901234567890"->repeat(11)) + exe "normal 6\<C-E>" + call s:check_mouse_click(5, 1, 1, 201) + call s:check_mouse_click(6, 1, 2, 1) + call s:check_mouse_click(7, 1, 3, 1) + + let &mouse = save_mouse + "let &term = save_term + "let &ttymouse = save_ttymouse +endfunc + +" this was dividing by zero +func Test_smoothscrol_zero_width() + CheckScreendump + + let lines =<< trim END + winsize 0 0 + vsplit + vsplit + vsplit + vsplit + vsplit + sil norm H + set wrap + set smoothscroll + set number + END + call writefile(lines, 'XSmoothScrollZero', 'D') + let buf = RunVimInTerminal('-u NONE -i NONE -n -m -X -Z -e -s -S XSmoothScrollZero', #{rows: 6, cols: 60, wait_for_ruler: 0}) + call TermWait(buf, 3000) + call VerifyScreenDump(buf, 'Test_smoothscroll_zero_1', {}) + + call term_sendkeys(buf, ":sil norm \<C-V>\<C-W>\<C-V>\<C-N>\<CR>") + call VerifyScreenDump(buf, 'Test_smoothscroll_zero_2', {}) + + call StopVimInTerminal(buf) +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_window_cmd.vim b/test/old/testdir/test_window_cmd.vim index f938203736..f18d1719c0 100644 --- a/test/old/testdir/test_window_cmd.vim +++ b/test/old/testdir/test_window_cmd.vim @@ -1734,7 +1734,7 @@ func Test_splitkeep_options() " let &t_WS = save_WS endfunc -function Test_splitkeep_cmdwin_cursor_position() +func Test_splitkeep_cmdwin_cursor_position() set splitkeep=screen call setline(1, range(&lines)) @@ -1759,9 +1759,9 @@ function Test_splitkeep_cmdwin_cursor_position() %bwipeout! set splitkeep& -endfunction +endfunc -function Test_splitkeep_misc() +func Test_splitkeep_misc() set splitkeep=screen set splitbelow @@ -1794,7 +1794,7 @@ function Test_splitkeep_misc() set splitkeep& endfunc -function Test_splitkeep_callback() +func Test_splitkeep_callback() CheckScreendump let lines =<< trim END set splitkeep=screen @@ -1827,7 +1827,7 @@ function Test_splitkeep_callback() call StopVimInTerminal(buf) endfunc -function Test_splitkeep_fold() +func Test_splitkeep_fold() CheckScreendump let lines =<< trim END @@ -1857,9 +1857,9 @@ function Test_splitkeep_fold() call VerifyScreenDump(buf, 'Test_splitkeep_fold_4', {}) call StopVimInTerminal(buf) -endfunction +endfunc -function Test_splitkeep_status() +func Test_splitkeep_status() CheckScreendump let lines =<< trim END @@ -1877,9 +1877,9 @@ function Test_splitkeep_status() call VerifyScreenDump(buf, 'Test_splitkeep_status_1', {}) call StopVimInTerminal(buf) -endfunction +endfunc -function Test_new_help_window_on_error() +func Test_new_help_window_on_error() help change.txt execute "normal! /CTRL-@\<CR>" silent! execute "normal! \<C-W>]" @@ -1889,7 +1889,26 @@ function Test_new_help_window_on_error() call assert_equal(wincount, winnr('$')) call assert_equal(expand("<cword>"), "'mod'") -endfunction +endfunc + +func Test_smoothscroll_in_zero_width_window() + let save_lines = &lines + let save_columns = &columns + + winsize 0 24 + set cpo+=n + exe "noremap 0 \<C-W>n\<C-W>L" + norm 000000 + set number smoothscroll + exe "norm \<C-Y>" + + only! + let &lines = save_lines + let &columns = save_columns + set cpo-=n + unmap 0 + set nonumber nosmoothscroll +endfunc " vim: shiftwidth=2 sts=2 expandtab |