diff options
61 files changed, 1336 insertions, 636 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcd6114d65..3653f04d5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,9 +6,10 @@ on: branches: 'master' jobs: - build: + unixish: name: ${{ matrix.os }} ${{ matrix.flavor }} (cc=${{ matrix.cc }}) strategy: + fail-fast: false matrix: include: - flavor: asan @@ -82,3 +83,27 @@ jobs: - name: Cache dependencies if: ${{ success() }} run: ./ci/before_cache.sh + + windows: + runs-on: windows-2016 + env: + DEPS_BUILD_DIR: "C:/projects/nvim-deps" + DEPS_PREFIX: "C:/projects/nvim-deps/usr" + + strategy: + fail-fast: false + matrix: + config: [ MINGW_64-gcov, MINGW_32, MSVC_64, MSVC_32 ] + name: windows (${{ matrix.config }}) + steps: + - uses: actions/checkout@v2 + + - uses: actions/cache@v2 + with: + path: C:\projects\nvim-deps + key: ${{ matrix.config }}-${{ hashFiles('third-party\**') }} + + - name: Run CI + run: powershell ci\build.ps1 + env: + CONFIGURATION: ${{ matrix.config }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6905f8dd35..7587f62e59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -105,8 +105,35 @@ jobs: path: build/release/nvim-macos.tar.gz retention-days: 1 + windows: + runs-on: windows-2016 + env: + DEPS_BUILD_DIR: "C:/projects/nvim-deps" + DEPS_PREFIX: "C:/projects/nvim-deps/usr" + strategy: + matrix: + include: + - config: MSVC_64 + archive: nvim-win64 + - config: MSVC_32 + archive: nvim-win32 + name: windows (${{ matrix.config }}) + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - run: powershell ci\build.ps1 -NoTests + env: + CONFIGURATION: ${{ matrix.config }} + - run: move build\Neovim.zip build\${{ matrix.archive }}.zip + - uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.archive }} + path: build/${{ matrix.archive }}.zip + retention-days: 1 + publish: - needs: [linux, appimage, macOS] + needs: [linux, appimage, macOS, windows] runs-on: ubuntu-20.04 steps: - uses: actions/download-artifact@v2 @@ -143,6 +170,8 @@ jobs: nvim-linux64.tar.gz:./nvim-linux64/nvim-linux64.tar.gz nvim.appimage:./appimage/nvim.appimage nvim.appimage.zsync:./appimage/nvim.appimage.zsync + nvim-win32.zip:./nvim-win32/nvim-win32.zip + nvim-win64.zip:./nvim-win64/nvim-win64.zip body: | ${{ env.SUBJECT }} ``` diff --git a/.travis.yml b/.travis.yml index b68f4f1bc1..06547febba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,24 +96,6 @@ addons: jobs: include: - - stage: baseline - name: clang-asan - os: linux - compiler: clang-11 - # Use Lua so that ASAN can test our embedded Lua support. 8fec4d53d0f6 - env: - - CLANG_SANITIZER=ASAN_UBSAN - - CMAKE_FLAGS="$CMAKE_FLAGS -DPREFER_LUA=ON" - - SYMBOLIZER=asan_symbolize-11 - - *common-job-env - addons: - apt: - sources: - - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' - key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' - packages: - - *common-apt-packages - - clang-11 - name: gcc-coverage (gcc 9) os: linux compiler: gcc-9 @@ -134,20 +116,6 @@ jobs: packages: - *common-apt-packages - gcc-9 - - if: branch = master AND commit_message !~ /\[skip.lint\]/ - name: lint - os: linux - env: - - CI_TARGET=lint - - *common-job-env - - - stage: second stage - name: "macOS: clang" - os: osx - compiler: clang - osx_image: xcode10.2 # macOS 10.14 - env: - - *common-job-env - name: gcc-functionaltest-lua os: linux compiler: gcc @@ -188,12 +156,6 @@ jobs: - python3-setuptools - python-dev - python3-dev - - name: clang-tsan - os: linux - compiler: clang - env: - - CLANG_SANITIZER=TSAN - - *common-job-env - if: type != pull_request name: snap os: linux @@ -5,7 +5,6 @@ [Twitter](https://twitter.com/Neovim) [](https://github.com/neovim/neovim/actions?query=workflow%3A%22CI%22) -[](https://ci.appveyor.com/project/neovim/neovim/branch/master) [](https://codecov.io/gh/neovim/neovim) [](https://scan.coverity.com/projects/2227) [](https://neovim.io/doc/reports/clang) diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 01ca16f930..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,39 +0,0 @@ -version: '{build}' -environment: - APPVEYOR_CACHE_ENTRY_ZIP_ARGS: "-t7z -m0=lzma -mx=9" - DEPS_BUILD_DIR: "C:/projects/nvim-deps" - DEPS_PREFIX: "C:/projects/nvim-deps/usr" - # Silence/redirect errors due to missing locking support (via libgcov). - GCOV_ERROR_FILE: "$(TEMP)/libgcov-errors.log" -image: Visual Studio 2017 -configuration: -- MINGW_64-gcov -- MINGW_32 -- MSVC_64 -- MSVC_32 -init: -- ps: | - # Pull requests: skip some build configurations to save time. - if ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT -and $env:CONFIGURATION -match '^(MSVC_64|MINGW_32)$') { - $env:APPVEYOR_CACHE_SKIP_SAVE = "true" - Exit-AppVeyorBuild - } -# RDP -#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) -matrix: - fast_finish: true -build_script: -- powershell ci\build.ps1 -after_build: -- ps: | - if (Test-Path $env:GCOV_ERROR_FILE) { - Get-Content $env:GCOV_ERROR_FILE -Head 10 - Get-Content $env:GCOV_ERROR_FILE -Tail 10 - } else { - write-host "no GCOV_ERROR_FILE" - } -cache: -- C:\projects\nvim-deps -> third-party\** -artifacts: -- path: build/Neovim.zip -- path: build/bin/nvim.exe diff --git a/ci/build.ps1 b/ci/build.ps1 index dbc43aecf3..db7026ac66 100644 --- a/ci/build.ps1 +++ b/ci/build.ps1 @@ -3,7 +3,6 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' -$isPullRequest = ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT -ne $null) $env:CONFIGURATION -match '^(?<compiler>\w+)_(?<bits>32|64)(?:-(?<option>\w+))?$' $compiler = $Matches.compiler $compileOption = if ($Matches -contains 'option') {$Matches.option} else {''} @@ -29,27 +28,9 @@ function exitIfFailed() { } } -# https://github.com/lukesampson/scoop#installation -$scoop = (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') -& { - Set-StrictMode -Off - Invoke-Expression $scoop -} - -scoop install perl -perl --version -cpanm.bat --version - if (-not $NoTests) { - scoop install nodejs-lts node --version npm.cmd --version - - cpanm.bat -n Neovim::Ext - if ($LastExitCode -ne 0) { - Get-Content -Path "$env:USERPROFILE\.cpanm\build.log" - } - perl -W -e 'use Neovim::Ext; print $Neovim::Ext::VERSION'; exitIfFailed } if (-Not (Test-Path -PathType container $env:DEPS_BUILD_DIR)) { @@ -91,14 +72,7 @@ if ($compiler -eq 'MINGW') { & C:\msys64\usr\bin\mkdir -p /var/cache/pacman/pkg # Build third-party dependencies - C:\msys64\usr\bin\bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" ; exitIfFailed - C:\msys64\usr\bin\bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" ; exitIfFailed - C:\msys64\usr\bin\bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" ; exitIfFailed - C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" ; exitIfFailed - # If there are still processes using msys-2.0.dll, after the base system update is finished, it will wait for input from the user. - # To prevent this, we will terminate all processes that use msys-2.0.dll. - Get-Process | Where-Object { $_.path -like 'C:\msys64*' } | Stop-Process - C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Syu" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Su" ; exitIfFailed C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm --needed -S $mingwPackages" ; exitIfFailed } elseif ($compiler -eq 'MSVC') { @@ -113,16 +87,16 @@ elseif ($compiler -eq 'MSVC') { if (-not $NoTests) { # Setup python (use AppVeyor system python) - C:\Python27\python.exe -m pip install pynvim ; exitIfFailed - C:\Python35\python.exe -m pip install pynvim ; exitIfFailed + + C:\hostedtoolcache\windows\Python\2.7.18\x64\python.exe -m pip install pynvim ; exitIfFailed + C:\hostedtoolcache\windows\Python\3.5.4\x64\python.exe -m pip install pynvim ; exitIfFailed # Disambiguate python3 - move c:\Python35\python.exe c:\Python35\python3.exe - $env:PATH = "C:\Python35;C:\Python27;$env:PATH" + move C:\hostedtoolcache\windows\Python\3.5.4\x64\python.exe C:\hostedtoolcache\windows\Python\3.5.4\x64\python3.exe + $env:PATH = "C:\hostedtoolcache\windows\Python\3.5.4\x64;C:\hostedtoolcache\windows\Python\2.7.18\x64;$env:PATH" # Sanity check python -c "import pynvim; print(str(pynvim))" ; exitIfFailed python3 -c "import pynvim; print(str(pynvim))" ; exitIfFailed - $env:PATH = "C:\Ruby24\bin;$env:PATH" gem.cmd install --pre neovim Get-Command -CommandType Application neovim-ruby-host.bat diff --git a/contrib/flake.nix b/contrib/flake.nix index a75e584075..86e4b37cfa 100644 --- a/contrib/flake.nix +++ b/contrib/flake.nix @@ -44,7 +44,7 @@ pythonEnv = legacyPkgs.python3; luacheck = legacyPkgs.luaPackages.luacheck; in - neovim-debug.overrideAttrs(oa: { + (neovim-debug.override({doCheck = true;})).overrideAttrs(oa: { cmakeFlags = oa.cmakeFlags ++ [ "-DLUACHECK_PRG=${luacheck}/bin/luacheck" "-DMIN_LOG_LEVEL=0" diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 7a3af78ab4..c4b4594290 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -376,7 +376,7 @@ on writing and loading a buffer to file, nor in undo/redo cycles. Highlights are registered using the |nvim_buf_add_highlight()| function. If an external highlighter plugin wants to add many highlights in a batch, performance can be improved by calling |nvim_buf_add_highlight()| as an -asynchronous notification, after first (synchronously) reqesting a source id. +asynchronous notification, after first (synchronously) requesting a source id. Example using the Python API client (|pynvim|): > diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 5c67359002..f3ed086933 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -954,9 +954,13 @@ inside of strings can change! Also see 'softtabstop' option. > delete and yank) ({.%#:} only work with put). *:reg* *:registers* -:reg[isters] Display the contents of all numbered and named - registers. If a register is written to for |:redir| - it will not be listed. +:reg[isters] Display the type and contents of all numbered and + named registers. If a register is written to for + |:redir| it will not be listed. + Type can be one of: + "c" for |characterwise| text + "l" for |linewise| text + "b" for |blockwise-visual| text :reg[isters] {arg} Display the contents of the numbered and named diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 67e2815715..8e93b188e9 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -118,7 +118,7 @@ FAQ *lsp-faq* < *vim.lsp.callbacks* - Q: What happened to `vim.lsp.callbacks`? - A: After better defining the interface of |lsp-hander|s, we thought it best + A: After better defining the interface of |lsp-handler|s, we thought it best to remove the generic usage of `callbacks` and transform to `handlers`. Due to this, `vim.lsp.callbacks` was renamed to |vim.lsp.handlers|. @@ -257,7 +257,7 @@ For |lsp-notification|, each |lsp-handler| has this signature: > *lsp-handler-configuration* -To configure the behavior of a builtin |lsp-handler|, the conenvience method +To configure the behavior of a builtin |lsp-handler|, the convenient method |vim.lsp.with()| is provided for users. To configure the behavior of |vim.lsp.diagnostic.on_publish_diagnostics()|, diff --git a/runtime/filetype.vim b/runtime/filetype.vim index ed3204c537..d2083b23d3 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -623,6 +623,9 @@ au BufNewFile,BufRead *.mo,*.gdmo setf gdmo " Gedcom au BufNewFile,BufRead *.ged,lltxxxxx.txt setf gedcom +" Gift (Moodle) +autocmd BufRead,BufNewFile *.gift setf gift + " Git au BufNewFile,BufRead COMMIT_EDITMSG,MERGE_MSG,TAG_EDITMSG setf gitcommit au BufNewFile,BufRead *.git/config,.gitconfig,/etc/gitconfig setf gitconfig diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index c98fde3696..072349b226 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -1044,6 +1044,8 @@ function M.display(diagnostics, bufnr, client_id, config) diagnostics = diagnostics or M.get(bufnr, client_id) + vim.api.nvim_command("doautocmd <nomodeline> User LspDiagnosticsChanged") + if not diagnostics or vim.tbl_isempty(diagnostics) then return end @@ -1062,8 +1064,6 @@ function M.display(diagnostics, bufnr, client_id, config) if signs_opts then M.set_signs(diagnostics, bufnr, client_id, nil, signs_opts) end - - vim.api.nvim_command("doautocmd <nomodeline> User LspDiagnosticsChanged") end -- }}} -- Diagnostic User Functions {{{ diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index a3bf61ba0b..fd23a6a547 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -63,8 +63,44 @@ local function progress_callback(_, _, params, client_id) vim.api.nvim_command("doautocmd <nomodeline> User LspProgressUpdate") end +--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress M['$/progress'] = progress_callback +--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create +M['window/workDoneProgress/create'] = function(_, _, params, client_id) + local client = vim.lsp.get_client_by_id(client_id) + local token = params.token -- string or number + if not client then + err_message("LSP[", client_id, "] client has shut down after sending the message") + end + client.messages.progress[token] = {} + return vim.NIL +end + +--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest +M['window/showMessageRequest'] = function(_, _, params) + + local actions = params.actions + print(params.message) + local option_strings = {params.message, "\nRequest Actions:"} + for i, action in ipairs(actions) do + local title = action.title:gsub('\r\n', '\\r\\n') + title = title:gsub('\n', '\\n') + table.insert(option_strings, string.format("%d. %s", i, title)) + end + + -- window/showMessageRequest can return either MessageActionItem[] or null. + local choice = vim.fn.inputlist(option_strings) + if choice < 1 or choice > #actions then + return vim.NIL + else + local action_chosen = actions[choice] + return { + title = action_chosen; + } + end +end + --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction M['textDocument/codeAction'] = function(_, _, actions) if actions == nil or vim.tbl_isempty(actions) then @@ -253,9 +289,8 @@ M['textDocument/signatureHelp'] = function(_, method, result) end --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight -M['textDocument/documentHighlight'] = function(_, _, result, _) +M['textDocument/documentHighlight'] = function(_, _, result, _, bufnr, _) if not result then return end - local bufnr = api.nvim_get_current_buf() util.buf_highlight_references(bufnr, result) end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index b785d2f586..b2d3d0641c 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -730,7 +730,15 @@ function protocol.make_client_capabilities() experimental = nil; window = { workDoneProgress = true; - } + showMessage = { + messageActionItem = { + additionalPropertiesSupport = false; + }; + }; + showDocument = { + support = false; + }; + }; } end diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index 7f12c0c798..9fba38a49f 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -719,9 +719,7 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, // half-shape current and previous character int shape_c = half_shape(prev_c); - // Save away current character - int curr_c = c; - + int curr_c; int curr_laa = A_firstc_laa(c, *c1p); int prev_laa = A_firstc_laa(prev_c, prev_c1); diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 2397af27cc..f41068ea70 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -89,6 +89,10 @@ static inline bool ascii_iswhite(int) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; +static inline bool ascii_iswhite_or_nul(int) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; + static inline bool ascii_isdigit(int) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; @@ -117,6 +121,14 @@ static inline bool ascii_iswhite(int c) return c == ' ' || c == '\t'; } +/// Checks if `c` is a space or tab character or NUL. +/// +/// @see {ascii_isdigit} +static inline bool ascii_iswhite_or_nul(int c) +{ + return ascii_iswhite(c) || c == NUL; +} + /// Check whether character is a decimal digit. /// /// Library isdigit() function is officially locale-dependent and, for diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index a5e8097133..93a03986e5 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1587,7 +1587,7 @@ void enter_buffer(buf_T *buf) need_fileinfo = true; // display file info after redraw } // check if file changed - (void)buf_check_timestamp(curbuf, false); + (void)buf_check_timestamp(curbuf); curwin->w_topline = 1; curwin->w_topfill = 0; @@ -4706,7 +4706,6 @@ do_arg_all( int keep_tabs // keep current tabs, for ":tab drop file" ) { - int i; char_u *opened; // Array of weight for which args are open: // 0: not opened // 1: opened in other tab @@ -4715,6 +4714,7 @@ do_arg_all( int opened_len; // length of opened[] int use_firstwin = false; // use first window for arglist + bool tab_drop_empty_window = false; int split_ret = OK; bool p_ea_save; alist_T *alist; // argument list to be used @@ -4762,6 +4762,7 @@ do_arg_all( win_T *wpnext = NULL; tpnext = curtab->tp_next; for (win_T *wp = firstwin; wp != NULL; wp = wpnext) { + int i; wpnext = wp->w_next; buf = wp->w_buffer; if (buf->b_ffname == NULL @@ -4867,14 +4868,15 @@ do_arg_all( last_curwin = curwin; last_curtab = curtab; win_enter(lastwin, false); - // ":drop all" should re-use an empty window to avoid "--remote-tab" + // ":tab drop file" should re-use an empty window to avoid "--remote-tab" // leaving an empty tab page when executed locally. if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1 && curbuf->b_ffname == NULL && !curbuf->b_changed) { use_firstwin = true; + tab_drop_empty_window = true; } - for (i = 0; i < count && i < opened_len && !got_int; i++) { + for (int i = 0; i < count && !got_int; i++) { if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) { arg_had_last = true; } @@ -4894,6 +4896,10 @@ do_arg_all( } } } else if (split_ret == OK) { + // trigger events for tab drop + if (tab_drop_empty_window && i == count - 1) { + autocmd_no_enter--; + } if (!use_firstwin) { // split current window p_ea_save = p_ea; p_ea = true; // use space from all windows @@ -4919,6 +4925,9 @@ do_arg_all( || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) + ECMD_OLDBUF, curwin); + if (tab_drop_empty_window && i == count - 1) { + autocmd_no_enter++; + } if (use_firstwin) { autocmd_no_leave++; } @@ -5470,7 +5479,9 @@ int buf_signcols(buf_T *buf) curline = sign->lnum; linesum = 0; } - linesum++; + if (sign->has_text_or_icon) { + linesum++; + } } if (linesum > buf->b_signcols_max) { buf->b_signcols_max = linesum; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 1cdf84f9d0..93bc34fa4b 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -805,7 +805,7 @@ static void diff_try_update(diffio_T *dio, for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) { buf = curtab->tp_diffbuf[idx_new]; if (buf_valid(buf)) { - buf_check_timestamp(buf, false); + buf_check_timestamp(buf); } } } @@ -1225,8 +1225,7 @@ void ex_diffpatch(exarg_T *eap) EMSG(_("E816: Cannot read patch output")); } else { if (curbuf->b_fname != NULL) { - newname = vim_strnsave(curbuf->b_fname, - (int)(STRLEN(curbuf->b_fname) + 4)); + newname = vim_strnsave(curbuf->b_fname, STRLEN(curbuf->b_fname) + 4); STRCAT(newname, ".new"); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 2d9dba839d..876e53e3cd 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -381,7 +381,7 @@ static void insert_enter(InsertState *s) // Need to recompute the cursor position, it might move when the cursor is // on a TAB or special character. - curs_columns(true); + curs_columns(curwin, true); // Enable langmap or IME, indicated by 'iminsert'. // Note that IME may enabled/disabled without us noticing here, thus the @@ -594,7 +594,7 @@ static int insert_check(VimState *state) if (curwin->w_wcol < s->mincol - curbuf->b_p_ts && curwin->w_wrow == curwin->w_winrow - + curwin->w_height_inner - 1 - get_scrolloff_value() + + curwin->w_height_inner - 1 - get_scrolloff_value(curwin) && (curwin->w_cursor.lnum != curwin->w_topline || curwin->w_topfill > 0)) { if (curwin->w_topfill > 0) { @@ -608,7 +608,7 @@ static int insert_check(VimState *state) } // May need to adjust w_topline to show the cursor. - update_topline(); + update_topline(curwin); s->did_backspace = false; @@ -1561,7 +1561,7 @@ void edit_putchar(int c, bool highlight) int attr; if (curwin->w_grid.chars != NULL || default_grid.chars != NULL) { - update_topline(); // just in case w_topline isn't valid + update_topline(curwin); // just in case w_topline isn't valid validate_cursor(); if (highlight) { attr = HL_ATTR(HLF_8); @@ -1677,7 +1677,7 @@ void display_dollar(colnr_T col) // If on the last byte of a multi-byte move to the first byte. char_u *p = get_cursor_line_ptr(); curwin->w_cursor.col -= utf_head_off(p, p + col); - curs_columns(false); // Recompute w_wrow and w_wcol + curs_columns(curwin, false); // Recompute w_wrow and w_wcol if (curwin->w_wcol < curwin->w_grid.Columns) { edit_putchar('$', false); dollar_vcol = curwin->w_virtcol; @@ -3423,7 +3423,7 @@ static void ins_compl_addleader(int c) xfree(compl_leader); compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col, - (int)(curwin->w_cursor.col - compl_col)); + curwin->w_cursor.col - compl_col); ins_compl_new_leader(); } @@ -3810,10 +3810,10 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) */ static buf_T *ins_compl_next_buf(buf_T *buf, int flag) { - static win_T *wp; + static win_T *wp = NULL; if (flag == 'w') { // just windows - if (buf == curbuf) { // first call for this flag/expansion + if (buf == curbuf || wp == NULL) { // first call for this flag/expansion wp = curwin; } assert(wp); @@ -5327,8 +5327,9 @@ static int ins_complete(int c, bool enable_pum) compl_curr_match->cp_number); edit_submode_extra = match_ref; edit_submode_highl = HLF_R; - if (dollar_vcol >= 0) - curs_columns(FALSE); + if (dollar_vcol >= 0) { + curs_columns(curwin, false); + } } } } @@ -6158,7 +6159,7 @@ internal_format ( curwin->w_p_lbr = has_lbr; if (!format_only && haveto_redraw) { - update_topline(); + update_topline(curwin); redraw_curbuf_later(VALID); } } @@ -6807,7 +6808,7 @@ cursor_up ( coladvance(curwin->w_curswant); if (upd_topline) { - update_topline(); // make sure curwin->w_topline is valid + update_topline(curwin); // make sure curwin->w_topline is valid } return OK; @@ -6858,7 +6859,7 @@ cursor_down ( coladvance(curwin->w_curswant); if (upd_topline) { - update_topline(); // make sure curwin->w_topline is valid + update_topline(curwin); // make sure curwin->w_topline is valid } return OK; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a7a860ba72..f60504de5e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -76,9 +76,6 @@ static char_u * const namespace_char = (char_u *)"abglstvw"; /// Variable used for g: static ScopeDictDictItem globvars_var; -/// g: value -#define globvarht globvardict.dv_hashtab - /* * Old Vim variables such as "v:version" are also available without the "v:". * Also in functions. We need a special hashtable for them. @@ -2556,6 +2553,7 @@ void free_for_info(void *fi_void) void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) + FUNC_ATTR_NONNULL_ALL { int got_eq = FALSE; int c; @@ -2638,6 +2636,23 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) } } } + + // ":exe one two" completes "two" + if ((cmdidx == CMD_execute + || cmdidx == CMD_echo + || cmdidx == CMD_echon + || cmdidx == CMD_echomsg) + && xp->xp_context == EXPAND_EXPRESSION) { + for (;;) { + char_u *const n = skiptowhite(arg); + + if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) { + break; + } + arg = skipwhite(n); + } + } + xp->xp_pattern = arg; } @@ -5404,7 +5419,7 @@ static int get_literal_key(char_u **arg, typval_T *tv) for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) { } tv->v_type = VAR_STRING; - tv->vval.v_string = vim_strnsave(*arg, (int)(p - *arg)); + tv->vval.v_string = vim_strnsave(*arg, p - *arg); *arg = skipwhite(p); return OK; @@ -7070,7 +7085,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, } } check_cursor_col(); - update_topline(); + update_topline(curwin); } if (!is_curbuf) { @@ -7782,13 +7797,13 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, if (name[0] == 'w' && dollar_lnum) { pos.col = 0; if (name[1] == '0') { // "w0": first visible line - update_topline(); + update_topline(curwin); // In silent Ex mode topline is zero, but that's not a valid line // number; use one instead. pos.lnum = curwin->w_topline > 0 ? curwin->w_topline : 1; return &pos; } else if (name[1] == '$') { // "w$": last visible line - validate_botline(); + validate_botline(curwin); // In silent Ex mode botline is zero, return zero then. pos.lnum = curwin->w_botline > 0 ? curwin->w_botline - 1 : 0; return &pos; @@ -10246,9 +10261,6 @@ repeat: if (src[*usedlen] == ':' && (src[*usedlen + 1] == 's' || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) { - char_u *str; - char_u *pat; - char_u *sub; int sep; char_u *flags; int didit = FALSE; @@ -10265,13 +10277,13 @@ repeat: // find end of pattern p = vim_strchr(s, sep); if (p != NULL) { - pat = vim_strnsave(s, (int)(p - s)); + char_u *const pat = vim_strnsave(s, p - s); s = p + 1; // find end of substitution p = vim_strchr(s, sep); if (p != NULL) { - sub = vim_strnsave(s, (int)(p - s)); - str = vim_strnsave(*fnamep, *fnamelen); + char_u *const sub = vim_strnsave(s, p - s); + char_u *const str = vim_strnsave(*fnamep, *fnamelen); *usedlen = (size_t)(p + 1 - src); s = do_string_sub(str, pat, sub, NULL, flags); *fnamep = s; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 89d9a85775..16eb6f8898 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4206,6 +4206,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = true; } else if (STRICMP(name, "syntax_items") == 0) { n = syntax_present(curwin); + } else if (STRICMP(name, "clipboard_working") == 0) { + n = eval_has_provider("clipboard"); #ifdef UNIX } else if (STRICMP(name, "unnamedplus") == 0) { n = eval_has_provider("clipboard"); @@ -6551,7 +6553,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (prevlen == 0) { assert(len < INT_MAX); - s = vim_strnsave(start, (int)len); + s = vim_strnsave(start, len); } else { /* Change "prev" buffer to be the right size. This way * the bytes are only copied once, and very long lines are @@ -10853,7 +10855,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - rettv->vval.v_string = vim_strnsave(head, (int)(tail - head)); + rettv->vval.v_string = vim_strnsave(head, tail - head); } /* diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 4e8b98d723..70c998ef39 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2297,9 +2297,9 @@ void ex_function(exarg_T *eap) // Ignore leading white space. p = skipwhite(p + 4); heredoc_trimmed = - vim_strnsave(theline, (int)(skipwhite(theline) - theline)); + vim_strnsave(theline, skipwhite(theline) - theline); } - skip_until = vim_strnsave(p, (int)(skiptowhite(p) - p)); + skip_until = vim_strnsave(p, skiptowhite(p) - p); do_concat = false; is_heredoc = true; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b23209e1b6..ae389a6727 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2323,7 +2323,7 @@ int do_ecmd( // Existing memfile. oldbuf = true; set_bufref(&bufref, buf); - (void)buf_check_timestamp(buf, false); + (void)buf_check_timestamp(buf); // Check if autocommands made buffer invalid or changed the current // buffer. if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf) { @@ -2706,7 +2706,7 @@ int do_ecmd( if (topline == 0 && command == NULL) { *so_ptr = 999; // force cursor to be vertically centered in the window } - update_topline(); + update_topline(curwin); curwin->w_scbind_pos = curwin->w_topline; *so_ptr = n; redraw_curbuf_later(NOT_VALID); // redraw this buffer later @@ -2796,9 +2796,10 @@ void ex_append(exarg_T *eap) p = vim_strchr(eap->nextcmd, NL); if (p == NULL) p = eap->nextcmd + STRLEN(eap->nextcmd); - theline = vim_strnsave(eap->nextcmd, (int)(p - eap->nextcmd)); - if (*p != NUL) - ++p; + theline = vim_strnsave(eap->nextcmd, p - eap->nextcmd); + if (*p != NUL) { + p++; + } eap->nextcmd = p; } else { // Set State to avoid the cursor shape to be set to INSERT mode @@ -3705,7 +3706,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, + len_change; highlight_match = TRUE; - update_topline(); + update_topline(curwin); validate_cursor(); update_screen(SOME_VALID); highlight_match = false; @@ -5740,7 +5741,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, redraw_later(curwin, SOME_VALID); win_enter(save_curwin, false); // Return to original window - update_topline(); + update_topline(curwin); // Update screen now. int save_rd = RedrawingDisabled; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 3b9c44c3cd..bde584d27e 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3392,7 +3392,7 @@ void ex_checktime(exarg_T *eap) } else { buf = buflist_findnr((int)eap->line2); if (buf != NULL) { // cannot happen? - (void)buf_check_timestamp(buf, false); + (void)buf_check_timestamp(buf); } } no_check_timestamps = save_no_check_timestamps; @@ -3790,6 +3790,14 @@ void ex_drop(exarg_T *eap) if (wp->w_buffer == buf) { goto_tabpage_win(tp, wp); curwin->w_arg_idx = 0; + if (!bufIsChanged(curbuf)) { + const int save_ar = curbuf->b_p_ar; + + // reload the file if it is newer + curbuf->b_p_ar = 1; + buf_check_timestamp(curbuf); + curbuf->b_p_ar = save_ar; + } return; } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a5eccc12b9..003c78b241 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2979,6 +2979,15 @@ const char * set_one_cmd_context( const char *arg = (const char *)skipwhite((const char_u *)p); + // Skip over ++argopt argument + if ((ea.argt & ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) { + p = arg; + while (*p && !ascii_isspace(*p)) { + MB_PTR_ADV(p); + } + arg = (const char *)skipwhite((const char_u *)p); + } + if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { if (*arg == '>') { // Append. if (*++arg == '>') { @@ -4760,10 +4769,8 @@ static void ex_abclear(exarg_T *eap) static void ex_autocmd(exarg_T *eap) { - /* - * Disallow auto commands from .exrc and .vimrc in current - * directory for security reasons. - */ + // Disallow autocommands from .exrc and .vimrc in current + // directory for security reasons. if (secure) { secure = 2; eap->errmsg = e_curdir; @@ -4979,7 +4986,6 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, FUNC_ATTR_NONNULL_ARG(1, 3) { ucmd_T *cmd = NULL; - char_u *p; int i; int cmp = 1; char_u *rep_buf = NULL; @@ -5039,7 +5045,7 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, if (cmp != 0) { ga_grow(gap, 1); - p = vim_strnsave(name, (int)name_len); + char_u *const p = vim_strnsave(name, name_len); cmd = USER_CMD_GA(gap, i); memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T)); @@ -6188,8 +6194,9 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, return FAIL; } - if (arg != NULL) - *compl_arg = vim_strnsave(arg, (int)arglen); + if (arg != NULL) { + *compl_arg = vim_strnsave(arg, arglen); + } return OK; } @@ -7362,7 +7369,7 @@ static void ex_syncbind(exarg_T *eap) topline = curwin->w_topline; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_p_scb && wp->w_buffer) { - y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value(); + y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value(curwin); if (topline > y) { topline = y; } @@ -8050,7 +8057,7 @@ static void ex_redraw(exarg_T *eap) RedrawingDisabled = 0; p_lz = FALSE; validate_cursor(); - update_topline(); + update_topline(curwin); if (eap->forceit) { redraw_all_later(NOT_VALID); } @@ -8199,10 +8206,11 @@ static void ex_mark(exarg_T *eap) */ void update_topline_cursor(void) { - check_cursor(); /* put cursor on valid line */ - update_topline(); - if (!curwin->w_p_wrap) + check_cursor(); // put cursor on valid line + update_topline(curwin); + if (!curwin->w_p_wrap) { validate_cursor(); + } update_curswant(); } @@ -9282,7 +9290,7 @@ static void ex_match(exarg_T *eap) } else { p = skiptowhite(eap->arg); if (!eap->skip) { - g = vim_strnsave(eap->arg, (int)(p - eap->arg)); + g = vim_strnsave(eap->arg, p - eap->arg); } p = skipwhite(p); if (*p == NUL) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 53dd2776c1..0f50d5153d 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -523,7 +523,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, // positioned in the same way as the actual search command restore_viewstate(&s->old_viewstate); changed_cline_bef_curs(); - update_topline(); + update_topline(curwin); if (found != 0) { pos_T save_pos = curwin->w_cursor; @@ -1082,6 +1082,7 @@ static int command_line_execute(VimState *state, int key) if (s->c == K_DOWN && ccline.cmdpos > 0 && ccline.cmdbuff[ccline.cmdpos - 1] == '.') { s->c = (int)p_wc; + KeyTyped = true; // in case the key was mapped } else if (s->c == K_UP) { // Hitting <Up>: Remove one submenu name in front of the // cursor @@ -1112,6 +1113,7 @@ static int command_line_execute(VimState *state, int key) cmdline_del(i); } s->c = (int)p_wc; + KeyTyped = true; // in case the key was mapped s->xpc.xp_context = EXPAND_NOTHING; } } @@ -1134,6 +1136,7 @@ static int command_line_execute(VimState *state, int key) || ccline.cmdbuff[ccline.cmdpos - 3] != '.')) { // go down a directory s->c = (int)p_wc; + KeyTyped = true; // in case the key was mapped } else if (STRNCMP(s->xpc.xp_pattern, upseg + 1, 3) == 0 && s->c == K_DOWN) { // If in a direct ancestor, strip off one ../ to go down @@ -1154,6 +1157,7 @@ static int command_line_execute(VimState *state, int key) && (vim_ispathsep(ccline.cmdbuff[j - 3]) || j == i + 2)) { cmdline_del(j - 2); s->c = (int)p_wc; + KeyTyped = true; // in case the key was mapped } } else if (s->c == K_UP) { // go up a directory @@ -1546,7 +1550,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, set_search_match(&s->match_end); curwin->w_cursor = s->match_start; changed_cline_bef_curs(); - update_topline(); + update_topline(curwin); validate_cursor(); highlight_match = true; save_viewstate(&s->old_viewstate); @@ -2235,7 +2239,7 @@ static int command_line_changed(CommandLineState *s) // Restore the window "view". curwin->w_cursor = s->is_state.save_cursor; restore_viewstate(&s->is_state.old_viewstate); - update_topline(); + update_topline(curwin); redrawcmdline(); @@ -2244,7 +2248,9 @@ static int command_line_changed(CommandLineState *s) close_preview_windows(); update_screen(SOME_VALID); // Clear 'inccommand' preview. } else { - may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); + if (s->xpc.xp_context == EXPAND_NOTHING) { + may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); + } } if (cmdmsg_rl || (p_arshape && !p_tbidi)) { diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index d831ffc050..14dac9a126 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -526,8 +526,12 @@ static int makeopens(FILE *fd, char_u *dirnow) } } - // Close all windows but one. + // Close all windows and tabs but one. PUTLINE_FAIL("silent only"); + if ((ssop_flags & SSOP_TABPAGES) + && put_line(fd, "silent tabonly") == FAIL) { + return FAIL; + } // // Now a :cd command to the session directory or the current directory @@ -606,13 +610,26 @@ static int makeopens(FILE *fd, char_u *dirnow) // tab_firstwin = firstwin; // First window in tab page "tabnr". tab_topframe = topframe; + if ((ssop_flags & SSOP_TABPAGES)) { + // Similar to ses_win_rec() below, populate the tab pages first so + // later local options won't be copied to the new tabs. + FOR_ALL_TABS(tp) { + if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL) { + return FAIL; + } + } + + if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL) { + return FAIL; + } + } for (tabnr = 1;; tabnr++) { tabpage_T *tp = find_tabpage(tabnr); if (tp == NULL) { break; // done all tab pages } - int need_tabnew = false; + bool need_tabnext = false; int cnr = 1; if ((ssop_flags & SSOP_TABPAGES)) { @@ -624,7 +641,7 @@ static int makeopens(FILE *fd, char_u *dirnow) tab_topframe = tp->tp_topframe; } if (tabnr > 1) { - need_tabnew = true; + need_tabnext = true; } } @@ -639,11 +656,15 @@ static int makeopens(FILE *fd, char_u *dirnow) && !bt_help(wp->w_buffer) && !bt_nofile(wp->w_buffer) ) { - if (fputs(need_tabnew ? "tabedit " : "edit ", fd) < 0 + if (need_tabnext && put_line(fd, "tabnext") == FAIL) { + return FAIL; + } + need_tabnext = false; + + if (fputs("edit ", fd) < 0 || ses_fname(fd, wp->w_buffer, &ssop_flags, true) == FAIL) { return FAIL; } - need_tabnew = false; if (!wp->w_arg_idx_invalid) { edited_win = wp; } @@ -652,7 +673,7 @@ static int makeopens(FILE *fd, char_u *dirnow) } // If no file got edited create an empty tab page. - if (need_tabnew && put_line(fd, "tabnew") == FAIL) { + if (need_tabnext && put_line(fd, "tabnext") == FAIL) { return FAIL; } @@ -775,6 +796,7 @@ static int makeopens(FILE *fd, char_u *dirnow) // if (fprintf(fd, "%s", "if exists('s:wipebuf') " + "&& len(win_findbuf(s:wipebuf)) == 0" "&& getbufvar(s:wipebuf, '&buftype') isnot# 'terminal'\n" " silent exe 'bwipe ' . s:wipebuf\n" "endif\n" diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 3a84b5d41c..a542bb19dd 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1652,9 +1652,6 @@ failed: # ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { iconv_close(iconv_fd); -# ifndef __clang_analyzer__ - iconv_fd = (iconv_t)-1; -# endif } # endif @@ -2032,7 +2029,7 @@ static char_u *next_fenc(char_u **pp, bool *alloced) r = enc_canonize(*pp); *pp += STRLEN(*pp); } else { - r = vim_strnsave(*pp, (int)(p - *pp)); + r = vim_strnsave(*pp, p - *pp); *pp = p + 1; p = enc_canonize(r); xfree(r); @@ -4675,7 +4672,6 @@ check_timestamps( ) { int didit = 0; - int n; /* Don't check timestamps while system() or another low-level function may * cause us to lose and gain focus. */ @@ -4703,7 +4699,7 @@ check_timestamps( if (buf->b_nwindows > 0) { bufref_T bufref; set_bufref(&bufref, buf); - n = buf_check_timestamp(buf, focus); + const int n = buf_check_timestamp(buf); if (didit < n) { didit = n; } @@ -4773,11 +4769,7 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) * return 2 if a message has been displayed. * return 0 otherwise. */ -int -buf_check_timestamp( - buf_T *buf, - int focus /* called for GUI focus event */ -) +int buf_check_timestamp(buf_T *buf) FUNC_ATTR_NONNULL_ALL { int retval = 0; @@ -5105,8 +5097,8 @@ void buf_reload(buf_T *buf, int orig_mode) curwin->w_topline = old_topline; curwin->w_cursor = old_cursor; check_cursor(); - update_topline(); - keep_filetype = FALSE; + update_topline(curwin); + keep_filetype = false; /* Update folds unless they are defined manually. */ FOR_ALL_TAB_WINDOWS(tp, wp) { diff --git a/src/nvim/globals.h b/src/nvim/globals.h index c53c1546a4..31b905e858 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -215,6 +215,8 @@ EXTERN bool emsg_severe INIT(= false); // use message of next of several EXTERN int did_endif INIT(= false); // just had ":endif" EXTERN dict_T vimvardict; // Dictionary with v: variables EXTERN dict_T globvardict; // Dictionary with g: variables +/// g: value +#define globvarht globvardict.dv_hashtab EXTERN int did_emsg; // set by emsg() when the message // is displayed or thrown EXTERN bool called_vim_beep; // set if vim_beep() is called diff --git a/src/nvim/main.c b/src/nvim/main.c index 41ae78e996..8bf745966e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -539,7 +539,7 @@ int main(int argc, char **argv) // When a startup script or session file setup for diff'ing and // scrollbind, sync the scrollbind now. if (curwin->w_p_diff && curwin->w_p_scb) { - update_topline(); + update_topline(curwin); check_scrollbind((linenr_T)0, 0L); TIME_MSG("diff scrollbinding"); } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 70225484ec..31dc6b3649 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -975,9 +975,9 @@ void ml_recover(bool checkext) if (b0p->b0_flags & B0_HAS_FENC) { int fnsize = B0_FNAME_SIZE_NOCRYPT; - for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p) - ; - b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + fnsize - p)); + for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) { + } + b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p); } mf_put(mfp, hp, false, false); /* release block 0 */ diff --git a/src/nvim/move.c b/src/nvim/move.c index fdcf6bb189..a6afdc27d9 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -129,98 +129,99 @@ void redraw_for_cursorline(win_T *wp) */ void update_topline_redraw(void) { - update_topline(); - if (must_redraw) + update_topline(curwin); + if (must_redraw) { update_screen(0); + } } /* * Update curwin->w_topline to move the cursor onto the screen. */ -void update_topline(void) +void update_topline(win_T *wp) { linenr_T old_topline; int old_topfill; bool check_topline = false; bool check_botline = false; - long *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so; + long *so_ptr = wp->w_p_so >= 0 ? &wp->w_p_so : &p_so; long save_so = *so_ptr; // If there is no valid screen and when the window height is zero just use // the cursor line. - if (!default_grid.chars || curwin->w_height_inner == 0) { - curwin->w_topline = curwin->w_cursor.lnum; - curwin->w_botline = curwin->w_topline; - curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; - curwin->w_viewport_invalid = true; - curwin->w_scbind_pos = 1; + if (!default_grid.chars || wp->w_height_inner == 0) { + wp->w_topline = wp->w_cursor.lnum; + wp->w_botline = wp->w_topline; + wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; + wp->w_viewport_invalid = true; + wp->w_scbind_pos = 1; return; } - check_cursor_moved(curwin); - if (curwin->w_valid & VALID_TOPLINE) + check_cursor_moved(wp); + if (wp->w_valid & VALID_TOPLINE) { return; + } // When dragging with the mouse, don't scroll that quickly if (mouse_dragging > 0) { *so_ptr = mouse_dragging - 1; } - old_topline = curwin->w_topline; - old_topfill = curwin->w_topfill; + old_topline = wp->w_topline; + old_topfill = wp->w_topfill; // If the buffer is empty, always set topline to 1. if (BUFEMPTY()) { // special case - file is empty - if (curwin->w_topline != 1) { - redraw_later(curwin, NOT_VALID); + if (wp->w_topline != 1) { + redraw_later(wp, NOT_VALID); } - curwin->w_topline = 1; - curwin->w_botline = 2; - curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; - curwin->w_viewport_invalid = true; - curwin->w_scbind_pos = 1; - } - /* - * 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. - */ - else { - if (curwin->w_topline > 1) { - /* 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. */ - if (curwin->w_cursor.lnum < curwin->w_topline) + wp->w_topline = 1; + wp->w_botline = 2; + wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; + wp->w_viewport_invalid = true; + wp->w_scbind_pos = 1; + } else { + // 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 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. + if (wp->w_cursor.lnum < wp->w_topline) { check_topline = true; - else if (check_top_offset()) + } else if (check_top_offset()) { check_topline = true; + } } - /* Check if there are more filler lines than allowed. */ - if (!check_topline && curwin->w_topfill > diff_check_fill(curwin, - curwin->w_topline)) + // Check if there are more filler lines than allowed. + if (!check_topline && wp->w_topfill > diff_check_fill(wp, wp->w_topline)) { check_topline = true; + } if (check_topline) { - int halfheight = curwin->w_height_inner / 2 - 1; + int halfheight = wp->w_height_inner / 2 - 1; if (halfheight < 2) { halfheight = 2; } long n; - if (hasAnyFolding(curwin)) { - /* Count the number of logical lines between the cursor and - * topline + p_so (approximation of how much will be - * scrolled). */ + if (hasAnyFolding(wp)) { + // Count the number of logical lines between the cursor and + // topline + p_so (approximation of how much will be + // scrolled). n = 0; - for (linenr_T lnum = curwin->w_cursor.lnum; - lnum < curwin->w_topline + *so_ptr; lnum++) { + for (linenr_T lnum = wp->w_cursor.lnum; + lnum < wp->w_topline + *so_ptr; lnum++) { n++; // stop at end of file or when we know we are far off - if (lnum >= curbuf->b_ml.ml_line_count || n >= halfheight) { + assert(wp->w_buffer != 0); + if (lnum >= wp->w_buffer->b_ml.ml_line_count || n >= halfheight) { break; } - (void)hasFolding(lnum, NULL, &lnum); + (void)hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); } } else { - n = curwin->w_topline + *so_ptr - curwin->w_cursor.lnum; + n = wp->w_topline + *so_ptr - wp->w_cursor.lnum; } /* If we weren't very close to begin with, we scroll to put the @@ -233,8 +234,8 @@ void update_topline(void) check_botline = true; } } else { - /* Make sure topline is the first line of a fold. */ - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + // Make sure topline is the first line of a fold. + (void)hasFoldingWin(wp, wp->w_topline, &wp->w_topline, NULL, true, NULL); check_botline = true; } } @@ -248,35 +249,36 @@ void update_topline(void) * for every small change. */ if (check_botline) { - if (!(curwin->w_valid & VALID_BOTLINE_AP)) - validate_botline(); - - if (curwin->w_botline <= curbuf->b_ml.ml_line_count) { - if (curwin->w_cursor.lnum < curwin->w_botline) { - if (((long)curwin->w_cursor.lnum - >= (long)curwin->w_botline - *so_ptr - || hasAnyFolding(curwin) - )) { + if (!(wp->w_valid & VALID_BOTLINE_AP)) { + validate_botline(wp); + } + + assert(wp->w_buffer != 0); + if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) { + if (wp->w_cursor.lnum < wp->w_botline) { + if (((long)wp->w_cursor.lnum + >= (long)wp->w_botline - *so_ptr + || hasAnyFolding(wp))) { lineoff_T loff; /* Cursor is (a few lines) above botline, check if there are * 'scrolloff' window lines below the cursor. If not, need to * scroll. */ - int n = curwin->w_empty_rows; - loff.lnum = curwin->w_cursor.lnum; - /* In a fold go to its last line. */ - (void)hasFolding(loff.lnum, NULL, &loff.lnum); + int n = wp->w_empty_rows; + loff.lnum = wp->w_cursor.lnum; + // In a fold go to its last line. + (void)hasFoldingWin(wp, loff.lnum, NULL, &loff.lnum, true, NULL); loff.fill = 0; - n += curwin->w_filler_rows; + n += wp->w_filler_rows; loff.height = 0; - while (loff.lnum < curwin->w_botline - && (loff.lnum + 1 < curwin->w_botline || loff.fill == 0) + while (loff.lnum < wp->w_botline + && (loff.lnum + 1 < wp->w_botline || loff.fill == 0) ) { n += loff.height; if (n >= *so_ptr) { break; } - botline_forw(&loff); + botline_forw(wp, &loff); } if (n >= *so_ptr) { // sufficient context, no need to scroll @@ -289,23 +291,23 @@ void update_topline(void) } if (check_botline) { long line_count = 0; - if (hasAnyFolding(curwin)) { - /* Count the number of logical lines between the cursor and - * botline - p_so (approximation of how much will be - * scrolled). */ - for (linenr_T lnum = curwin->w_cursor.lnum; - lnum >= curwin->w_botline - *so_ptr; lnum--) { + if (hasAnyFolding(wp)) { + // Count the number of logical lines between the cursor and + // botline - p_so (approximation of how much will be + // scrolled). + for (linenr_T lnum = wp->w_cursor.lnum; + lnum >= wp->w_botline - *so_ptr; lnum--) { line_count++; // stop at end of file or when we know we are far off - if (lnum <= 0 || line_count > curwin->w_height_inner + 1) { + if (lnum <= 0 || line_count > wp->w_height_inner + 1) { break; } (void)hasFolding(lnum, &lnum, NULL); } - } else - line_count = curwin->w_cursor.lnum - curwin->w_botline - + 1 + *so_ptr; - if (line_count <= curwin->w_height_inner + 1) { + } else { + line_count = wp->w_cursor.lnum - wp->w_botline + 1 + *so_ptr; + } + if (line_count <= wp->w_height_inner + 1) { scroll_cursor_bot(scrolljump_value(), false); } else { scroll_cursor_halfway(false); @@ -313,25 +315,25 @@ void update_topline(void) } } } - curwin->w_valid |= VALID_TOPLINE; - curwin->w_viewport_invalid = true; - win_check_anchored_floats(curwin); + wp->w_valid |= VALID_TOPLINE; + wp->w_viewport_invalid = true; + win_check_anchored_floats(wp); /* * Need to redraw when topline changed. */ - if (curwin->w_topline != old_topline - || curwin->w_topfill != old_topfill + if (wp->w_topline != old_topline + || wp->w_topfill != old_topfill ) { dollar_vcol = -1; - if (curwin->w_skipcol != 0) { - curwin->w_skipcol = 0; - redraw_later(curwin, NOT_VALID); + if (wp->w_skipcol != 0) { + wp->w_skipcol = 0; + redraw_later(wp, NOT_VALID); } else { - redraw_later(curwin, VALID); + redraw_later(wp, VALID); } // May need to set w_skipcol when cursor in w_topline. - if (curwin->w_cursor.lnum == curwin->w_topline) { + if (wp->w_cursor.lnum == wp->w_topline) { validate_cursor(); } } @@ -346,7 +348,7 @@ void update_topline_win(win_T* win) { win_T *save_curwin; switch_win(&save_curwin, NULL, win, NULL, true); - update_topline(); + update_topline(curwin); restore_win(save_curwin, NULL, true); } @@ -368,7 +370,7 @@ static int scrolljump_value(void) */ static bool check_top_offset(void) { - long so = get_scrolloff_value(); + long so = get_scrolloff_value(curwin); if (curwin->w_cursor.lnum < curwin->w_topline + so || hasAnyFolding(curwin) ) { @@ -378,7 +380,7 @@ static bool check_top_offset(void) int n = curwin->w_topfill; // always have this context // Count the visible screen lines above the cursor line. while (n < so) { - topline_back(&loff); + topline_back(curwin, &loff); // Stop when included a line above the window. if (loff.lnum < curwin->w_topline || (loff.lnum == curwin->w_topline && loff.fill > 0) @@ -498,10 +500,11 @@ void changed_line_abv_curs_win(win_T *wp) /* * Make sure the value of curwin->w_botline is valid. */ -void validate_botline(void) +void validate_botline(win_T *wp) { - if (!(curwin->w_valid & VALID_BOTLINE)) - comp_botline(curwin); + if (!(wp->w_valid & VALID_BOTLINE)) { + comp_botline(wp); + } } /* @@ -539,8 +542,9 @@ int cursor_valid(void) void validate_cursor(void) { check_cursor_moved(curwin); - if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) - curs_columns(true); + if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) { + curs_columns(curwin, true); + } } /* @@ -720,8 +724,10 @@ int curwin_col_off2(void) // Compute curwin->w_wcol and curwin->w_virtcol. // Also updates curwin->w_wrow and curwin->w_cline_row. // Also updates curwin->w_leftcol. +// @param may_scroll when true, may scroll horizontally void curs_columns( - int may_scroll /* when true, may scroll horizontally */ + win_T *wp, + int may_scroll ) { int n; @@ -729,136 +735,136 @@ void curs_columns( colnr_T startcol; colnr_T endcol; colnr_T prev_skipcol; - long so = get_scrolloff_value(); - long siso = get_sidescrolloff_value(); + 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(); + update_topline(wp); // Next make sure that w_cline_row is valid. - if (!(curwin->w_valid & VALID_CROW)) { - curs_rows(curwin); + if (!(wp->w_valid & VALID_CROW)) { + curs_rows(wp); } /* * Compute the number of virtual columns. */ - if (curwin->w_cline_folded) - /* In a folded line the cursor is always in the first column */ - startcol = curwin->w_virtcol = endcol = curwin->w_leftcol; - else - getvvcol(curwin, &curwin->w_cursor, - &startcol, &(curwin->w_virtcol), &endcol); + if (wp->w_cline_folded) { + // In a folded line the cursor is always in the first column + startcol = wp->w_virtcol = endcol = wp->w_leftcol; + } else { + getvvcol(wp, &wp->w_cursor, &startcol, &(wp->w_virtcol), &endcol); + } /* remove '$' from change command when cursor moves onto it */ if (startcol > dollar_vcol) dollar_vcol = -1; - int extra = curwin_col_off(); - curwin->w_wcol = curwin->w_virtcol + extra; + int extra = win_col_off(wp); + wp->w_wcol = wp->w_virtcol + extra; endcol += extra; - /* - * Now compute w_wrow, counting screen lines from w_cline_row. - */ - curwin->w_wrow = curwin->w_cline_row; + // Now compute w_wrow, counting screen lines from w_cline_row. + wp->w_wrow = wp->w_cline_row; - int textwidth = curwin->w_width_inner - extra; + int textwidth = wp->w_width_inner - extra; if (textwidth <= 0) { // No room for text, put cursor in last char of window. - curwin->w_wcol = curwin->w_width_inner - 1; - curwin->w_wrow = curwin->w_height_inner - 1; - } else if (curwin->w_p_wrap - && curwin->w_width_inner != 0 + wp->w_wcol = wp->w_width_inner - 1; + wp->w_wrow = wp->w_height_inner - 1; + } else if (wp->w_p_wrap + && wp->w_width_inner != 0 ) { - width = textwidth + curwin_col_off2(); + width = textwidth + win_col_off2(wp); - // long line wrapping, adjust curwin->w_wrow - if (curwin->w_wcol >= curwin->w_width_inner) { + // 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 = (curwin->w_wcol - curwin->w_width_inner) / width + 1; - curwin->w_wcol -= n * width; - curwin->w_wrow += n; + n = (wp->w_wcol - wp->w_width_inner) / width + 1; + wp->w_wcol -= n * width; + wp->w_wrow += n; /* When cursor wraps to first char of next line in Insert * mode, the 'showbreak' string isn't shown, backup to first * column */ if (*p_sbr && *get_cursor_pos_ptr() == NUL - && curwin->w_wcol == (int)vim_strsize(p_sbr)) - curwin->w_wcol = 0; + && wp->w_wcol == (int)vim_strsize(p_sbr)) { + wp->w_wcol = 0; + } } - } - /* No line wrapping: compute curwin->w_leftcol if scrolling is on and line - * is not folded. - * If scrolling is off, curwin->w_leftcol is assumed to be 0 */ - else if (may_scroll - && !curwin->w_cline_folded - ) { - /* - * If Cursor is left of the screen, scroll rightwards. - * If Cursor is right of the screen, scroll leftwards - * If we get closer to the edge than 'sidescrolloff', scroll a little - * extra - */ + } else if (may_scroll + && !wp->w_cline_folded + ) { + // No line wrapping: compute wp->w_leftcol if scrolling is on and line + // is not folded. + // If scrolling is off, wp->w_leftcol is assumed to be 0 + + // If Cursor is left of the screen, scroll rightwards. + // If Cursor is right of the screen, scroll leftwards + // If we get closer to the edge than 'sidescrolloff', scroll a little + // extra assert(siso <= INT_MAX); - int off_left = startcol - curwin->w_leftcol - (int)siso; + int off_left = startcol - wp->w_leftcol - (int)siso; int off_right = - endcol - curwin->w_leftcol - curwin->w_width_inner + (int)siso + 1; + endcol - wp->w_leftcol - wp->w_width_inner + (int)siso + 1; if (off_left < 0 || off_right > 0) { int diff = (off_left < 0) ? -off_left: off_right; /* 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 = curwin->w_wcol - extra - textwidth / 2; - else { + if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left) { + new_leftcol = wp->w_wcol - extra - textwidth / 2; + } else { if (diff < p_ss) { assert(p_ss <= INT_MAX); diff = (int)p_ss; } - if (off_left < 0) - new_leftcol = curwin->w_leftcol - diff; - else - new_leftcol = curwin->w_leftcol + diff; + if (off_left < 0) { + new_leftcol = wp->w_leftcol - diff; + } else { + new_leftcol = wp->w_leftcol + diff; + } } if (new_leftcol < 0) new_leftcol = 0; - if (new_leftcol != (int)curwin->w_leftcol) { - curwin->w_leftcol = new_leftcol; - win_check_anchored_floats(curwin); - // screen has to be redrawn with new curwin->w_leftcol - redraw_later(curwin, NOT_VALID); + if (new_leftcol != (int)wp->w_leftcol) { + wp->w_leftcol = new_leftcol; + win_check_anchored_floats(wp); + // screen has to be redrawn with new wp->w_leftcol + redraw_later(wp, NOT_VALID); } } - curwin->w_wcol -= curwin->w_leftcol; - } else if (curwin->w_wcol > (int)curwin->w_leftcol) - curwin->w_wcol -= curwin->w_leftcol; - else - curwin->w_wcol = 0; + wp->w_wcol -= wp->w_leftcol; + } else if (wp->w_wcol > (int)wp->w_leftcol) { + wp->w_wcol -= wp->w_leftcol; + } else { + wp->w_wcol = 0; + } /* Skip over filler lines. At the top use w_topfill, there * may be some filler lines above the window. */ - if (curwin->w_cursor.lnum == curwin->w_topline) - curwin->w_wrow += curwin->w_topfill; - else - curwin->w_wrow += diff_check_fill(curwin, curwin->w_cursor.lnum); + if (wp->w_cursor.lnum == wp->w_topline) { + wp->w_wrow += wp->w_topfill; + } else { + wp->w_wrow += diff_check_fill(wp, wp->w_cursor.lnum); + } - prev_skipcol = curwin->w_skipcol; + prev_skipcol = wp->w_skipcol; int plines = 0; - if ((curwin->w_wrow >= curwin->w_height_inner + if ((wp->w_wrow >= wp->w_height_inner || ((prev_skipcol > 0 - || curwin->w_wrow + so >= curwin->w_height_inner) + || wp->w_wrow + so >= wp->w_height_inner) && (plines = - plines_win_nofill(curwin, curwin->w_cursor.lnum, false)) - 1 - >= curwin->w_height_inner)) - && curwin->w_height_inner != 0 - && curwin->w_cursor.lnum == curwin->w_topline + plines_win_nofill(wp, wp->w_cursor.lnum, false)) - 1 + >= wp->w_height_inner)) + && wp->w_height_inner != 0 + && wp->w_cursor.lnum == wp->w_topline && width > 0 - && curwin->w_width_inner != 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 @@ -867,87 +873,88 @@ void curs_columns( * 2: Less than "p_so" lines below * 3: both of them */ extra = 0; - if (curwin->w_skipcol + so * width > curwin->w_virtcol) { + if (wp->w_skipcol + so * width > wp->w_virtcol) { extra = 1; } // Compute last display line of the buffer line that we want at the // bottom of the window. if (plines == 0) { - plines = plines_win(curwin, curwin->w_cursor.lnum, false); + plines = plines_win(wp, wp->w_cursor.lnum, false); } plines--; - if (plines > curwin->w_wrow + so) { + if (plines > wp->w_wrow + so) { assert(so <= INT_MAX); - n = curwin->w_wrow + (int)so; + n = wp->w_wrow + (int)so; } else { n = plines; } - if ((colnr_T)n >= curwin->w_height_inner + curwin->w_skipcol / width) { + if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width) { extra += 2; } if (extra == 3 || plines < so * 2) { // not enough room for 'scrolloff', put cursor in the middle - n = curwin->w_virtcol / width; - if (n > curwin->w_height_inner / 2) { - n -= curwin->w_height_inner / 2; + n = wp->w_virtcol / width; + if (n > wp->w_height_inner / 2) { + n -= wp->w_height_inner / 2; } else { n = 0; } // don't skip more than necessary - if (n > plines - curwin->w_height_inner + 1) { - n = plines - curwin->w_height_inner + 1; + if (n > plines - wp->w_height_inner + 1) { + n = plines - wp->w_height_inner + 1; } - curwin->w_skipcol = n * width; + wp->w_skipcol = n * width; } else if (extra == 1) { // less then 'scrolloff' lines above, decrease skipcol assert(so <= INT_MAX); - extra = (curwin->w_skipcol + (int)so * width - curwin->w_virtcol + extra = (wp->w_skipcol + (int)so * width - wp->w_virtcol + width - 1) / width; if (extra > 0) { - if ((colnr_T)(extra * width) > curwin->w_skipcol) - extra = curwin->w_skipcol / width; - curwin->w_skipcol -= extra * width; + if ((colnr_T)(extra * width) > wp->w_skipcol) { + extra = wp->w_skipcol / width; + } + wp->w_skipcol -= extra * width; } } else if (extra == 2) { // less then 'scrolloff' lines below, increase skipcol - endcol = (n - curwin->w_height_inner + 1) * width; - while (endcol > curwin->w_virtcol) { + endcol = (n - wp->w_height_inner + 1) * width; + while (endcol > wp->w_virtcol) { endcol -= width; } - if (endcol > curwin->w_skipcol) { - curwin->w_skipcol = endcol; + if (endcol > wp->w_skipcol) { + wp->w_skipcol = endcol; } } - curwin->w_wrow -= curwin->w_skipcol / width; - if (curwin->w_wrow >= curwin->w_height_inner) { + wp->w_wrow -= wp->w_skipcol / width; + if (wp->w_wrow >= wp->w_height_inner) { // small window, make sure cursor is in it - extra = curwin->w_wrow - curwin->w_height_inner + 1; - curwin->w_skipcol += extra * width; - curwin->w_wrow -= extra; + extra = wp->w_wrow - wp->w_height_inner + 1; + wp->w_skipcol += extra * width; + wp->w_wrow -= extra; } // extra could be either positive or negative - extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width; - win_scroll_lines(curwin, 0, extra); + extra = ((int)prev_skipcol - (int)wp->w_skipcol) / width; + win_scroll_lines(wp, 0, extra); } else { - curwin->w_skipcol = 0; + wp->w_skipcol = 0; } - if (prev_skipcol != curwin->w_skipcol) { - redraw_later(curwin, NOT_VALID); + if (prev_skipcol != wp->w_skipcol) { + redraw_later(wp, NOT_VALID); } - /* Redraw when w_virtcol changes and 'cursorcolumn' is set */ - if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0 + // Redraw when w_virtcol changes and 'cursorcolumn' is set + if (wp->w_p_cuc && (wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) { - redraw_later(curwin, SOME_VALID); + redraw_later(wp, SOME_VALID); } // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise - curwin->w_valid_leftcol = curwin->w_leftcol; + wp->w_valid_leftcol = wp->w_leftcol; - curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; + wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } /// Compute the screen position of text character at "pos" in window "wp" @@ -1231,7 +1238,7 @@ void scrolldown_clamp(void) end_row += curwin->w_cline_height - 1 - curwin->w_virtcol / curwin->w_width_inner; } - if (end_row < curwin->w_height_inner - get_scrolloff_value()) { + if (end_row < curwin->w_height_inner - get_scrolloff_value(curwin)) { if (can_fill) { ++curwin->w_topfill; check_topfill(curwin, true); @@ -1271,7 +1278,7 @@ void scrollup_clamp(void) validate_virtcol(); start_row -= curwin->w_virtcol / curwin->w_width_inner; } - if (start_row >= get_scrolloff_value()) { + if (start_row >= get_scrolloff_value(curwin)) { if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { @@ -1289,22 +1296,22 @@ void scrollup_clamp(void) * Returns the height of the added line in "lp->height". * Lines above the first one are incredibly high: MAXCOL. */ -static void topline_back(lineoff_T *lp) +static void topline_back(win_T *wp, lineoff_T *lp) { - if (lp->fill < diff_check_fill(curwin, lp->lnum)) { - /* Add a filler line. */ - ++lp->fill; + if (lp->fill < diff_check_fill(wp, lp->lnum)) { + // Add a filler line + lp->fill++; lp->height = 1; } else { --lp->lnum; lp->fill = 0; - if (lp->lnum < 1) + if (lp->lnum < 1) { lp->height = MAXCOL; - else if (hasFolding(lp->lnum, &lp->lnum, NULL)) - /* Add a closed fold */ + } else if (hasFolding(lp->lnum, &lp->lnum, NULL)) { + // Add a closed fold lp->height = 1; - else { - lp->height = plines_nofill(lp->lnum); + } else { + lp->height = plines_win_nofill(wp, lp->lnum, true); } } } @@ -1315,22 +1322,23 @@ static void topline_back(lineoff_T *lp) * Returns the height of the added line in "lp->height". * Lines below the last one are incredibly high. */ -static void botline_forw(lineoff_T *lp) +static void botline_forw(win_T *wp, lineoff_T *lp) { - if (lp->fill < diff_check_fill(curwin, lp->lnum + 1)) { - /* Add a filler line. */ - ++lp->fill; + if (lp->fill < diff_check_fill(wp, lp->lnum + 1)) { + // Add a filler line. + lp->fill++; lp->height = 1; } else { ++lp->lnum; lp->fill = 0; - if (lp->lnum > curbuf->b_ml.ml_line_count) { + assert(wp->w_buffer != 0); + if (lp->lnum > wp->w_buffer->b_ml.ml_line_count) { lp->height = MAXCOL; - } else if (hasFolding(lp->lnum, NULL, &lp->lnum)) { + } else if (hasFoldingWin(wp, lp->lnum, NULL, &lp->lnum, true, NULL)) { // Add a closed fold lp->height = 1; } else { - lp->height = plines_nofill(lp->lnum); + lp->height = plines_win_nofill(wp, lp->lnum, true); } } } @@ -1374,7 +1382,7 @@ void scroll_cursor_top(int min_scroll, int always) linenr_T old_topline = curwin->w_topline; linenr_T old_topfill = curwin->w_topfill; linenr_T new_topline; - int off = (int)get_scrolloff_value(); + int off = (int)get_scrolloff_value(curwin); if (mouse_dragging > 0) off = mouse_dragging - 1; @@ -1518,7 +1526,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) 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(); + long so = get_scrolloff_value(curwin); if (set_topbot) { used = 0; @@ -1528,7 +1536,7 @@ 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(&loff); + topline_back(curwin, &loff); if (loff.height == MAXCOL || used + loff.height > curwin->w_height_inner) { break; @@ -1542,8 +1550,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) || curwin->w_topfill != old_topfill ) curwin->w_valid &= ~(VALID_WROW|VALID_CROW); - } else - validate_botline(); + } else { + validate_botline(curwin); + } /* The lines of the cursor line itself are always used. */ used = plines_nofill(cln); @@ -1586,8 +1595,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) break; } - /* Add one line above */ - topline_back(&loff); + // Add one line above + topline_back(curwin, &loff); if (loff.height == MAXCOL) { used = MAXCOL; } else { @@ -1609,8 +1618,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } if (boff.lnum < curbuf->b_ml.ml_line_count) { - /* Add one line below */ - botline_forw(&boff); + // Add one line below + botline_forw(curwin, &boff); used += boff.height; if (used > curwin->w_height_inner) { break; @@ -1647,7 +1656,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) boff.lnum = curwin->w_topline - 1; int i; for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; ) { - botline_forw(&boff); + botline_forw(curwin, &boff); i += boff.height; ++line_count; } @@ -1701,7 +1710,7 @@ void scroll_cursor_halfway(int atend) while (topline > 1) { if (below <= above) { /* add a line below the cursor first */ if (boff.lnum < curbuf->b_ml.ml_line_count) { - botline_forw(&boff); + botline_forw(curwin, &boff); used += boff.height; if (used > curwin->w_height_inner) { break; @@ -1714,12 +1723,13 @@ void scroll_cursor_halfway(int atend) } } - if (below > above) { /* add a line above the cursor */ - topline_back(&loff); - if (loff.height == MAXCOL) + if (below > above) { // add a line above the cursor + topline_back(curwin, &loff); + if (loff.height == MAXCOL) { used = MAXCOL; - else + } else { used += loff.height; + } if (used > curwin->w_height_inner) { break; } @@ -1751,8 +1761,8 @@ void cursor_correct(void) * How many lines we would like to have above/below the cursor depends on * whether the first/last line of the file is on screen. */ - int above_wanted = (int)get_scrolloff_value(); - int below_wanted = (int)get_scrolloff_value(); + int above_wanted = (int)get_scrolloff_value(curwin); + int below_wanted = (int)get_scrolloff_value(curwin); if (mouse_dragging > 0) { above_wanted = mouse_dragging - 1; below_wanted = mouse_dragging - 1; @@ -1764,7 +1774,7 @@ void cursor_correct(void) below_wanted = max_off; } } - validate_botline(); + validate_botline(curwin); if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 && mouse_dragging == 0) { below_wanted = 0; @@ -1848,21 +1858,19 @@ int onepage(Direction dir, long count) int retval = OK; lineoff_T loff; linenr_T old_topline = curwin->w_topline; - long so = get_scrolloff_value(); + long so = get_scrolloff_value(curwin); if (curbuf->b_ml.ml_line_count == 1) { /* nothing to do */ beep_flush(); return FAIL; } - for (; count > 0; --count) { - validate_botline(); - /* - * It's an error to move a page up when the first line is already on - * the screen. It's an error to move a page down when the last line - * is on the screen and the topline is 'scrolloff' lines from the - * last line. - */ + for (; count > 0; count--) { + validate_botline(curwin); + // It's an error to move a page up when the first line is already on + // the screen. It's an error to move a page down when the last line + // is on the screen and the topline is 'scrolloff' lines from the + // last line. if (dir == FORWARD ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so) && curwin->w_botline > curbuf->b_ml.ml_line_count) @@ -1945,11 +1953,12 @@ int onepage(Direction dir, long count) * at the bottom of the window. */ n = 0; while (n <= curwin->w_height_inner && loff.lnum >= 1) { - topline_back(&loff); - if (loff.height == MAXCOL) + topline_back(curwin, &loff); + if (loff.height == MAXCOL) { n = MAXCOL; - else + } else { n += loff.height; + } } if (loff.lnum < 1) { /* at begin of file */ curwin->w_topline = 1; @@ -1958,11 +1967,11 @@ int onepage(Direction dir, long count) } else { /* Go two lines forward again. */ topline_botline(&loff); - botline_forw(&loff); - botline_forw(&loff); + botline_forw(curwin, &loff); + botline_forw(curwin, &loff); botline_topline(&loff); - /* We're at the wrong end of a fold now. */ - (void)hasFolding(loff.lnum, &loff.lnum, NULL); + // We're at the wrong end of a fold now. + (void)hasFoldingWin(curwin, loff.lnum, &loff.lnum, NULL, true, NULL); /* Always scroll at least one line. Avoid getting stuck on * very long lines. */ @@ -2046,10 +2055,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir) return; /* no overlap */ lineoff_T loff0 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); + if (dir > 0) { + botline_forw(curwin, lp); + } else { + topline_back(curwin, lp); + } int h2 = lp->height; if (h2 == MAXCOL || h2 + h1 > min_height) { *lp = loff0; /* no overlap */ @@ -2057,10 +2067,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir) } lineoff_T loff1 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); + if (dir > 0) { + botline_forw(curwin, lp); + } else { + topline_back(curwin, lp); + } int h3 = lp->height; if (h3 == MAXCOL || h3 + h2 > min_height) { *lp = loff0; /* no overlap */ @@ -2068,10 +2079,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir) } lineoff_T loff2 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); + if (dir > 0) { + botline_forw(curwin, lp); + } else { + topline_back(curwin, lp); + } int h4 = lp->height; if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height) *lp = loff1; /* 1 line overlap */ @@ -2094,8 +2106,8 @@ void halfpage(bool flag, linenr_T Prenum) int n = curwin->w_p_scr <= curwin->w_height_inner ? (int)curwin->w_p_scr : curwin->w_height_inner; - update_topline(); - validate_botline(); + update_topline(curwin); + validate_botline(curwin); int room = curwin->w_empty_rows + curwin->w_filler_rows; if (flag) { /* @@ -2255,7 +2267,7 @@ void do_check_cursorbind(void) // Only scroll when 'scrollbind' hasn't done this. if (!curwin->w_p_scb) { - update_topline(); + update_topline(curwin); } curwin->w_redr_status = true; } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 68ef4cd41e..a0b439ac45 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -377,6 +377,10 @@ static void request_event(void **argv) Channel *channel = e->channel; MsgpackRpcRequestHandler handler = e->handler; Error error = ERROR_INIT; + if (channel->rpc.closed) { + // channel was closed, abort any pending requests + goto free_ret; + } Object result = handler.fn(channel->id, e->args, &error); if (e->type == kMessageTypeRequest || ERROR_SET(&error)) { // Send the response. @@ -391,6 +395,8 @@ static void request_event(void **argv) } else { api_free_object(result); } + +free_ret: api_free_array(e->args); channel_decref(channel); xfree(e); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 3ecac615b3..4e955667dc 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -168,7 +168,7 @@ static const struct nv_cmd { { NL, nv_down, 0, false }, { Ctrl_K, nv_error, 0, 0 }, { Ctrl_L, nv_clear, 0, 0 }, - { Ctrl_M, nv_down, 0, true }, + { CAR, nv_down, 0, true }, { Ctrl_N, nv_down, NV_STS, false }, { Ctrl_O, nv_ctrlo, 0, 0 }, { Ctrl_P, nv_up, NV_STS, false }, @@ -1261,7 +1261,7 @@ static void normal_redraw(NormalState *s) { // Before redrawing, make sure w_topline is correct, and w_leftcol // if lines don't wrap, and w_skipcol if lines wrap. - update_topline(); + update_topline(curwin); validate_cursor(); // If the cursor moves horizontally when 'concealcursor' is active, then the @@ -1341,7 +1341,7 @@ static int normal_check(VimState *state) } else if (do_redraw || stuff_empty()) { // Need to make sure w_topline and w_leftcol are correct before // normal_check_window_scrolled() is called. - update_topline(); + update_topline(curwin); normal_check_cursor_moved(s); normal_check_text_changed(s); @@ -2629,7 +2629,7 @@ do_mouse ( /* Set global flag that we are extending the Visual area with mouse * dragging; temporarily minimize 'scrolloff'. */ - if (VIsual_active && is_drag && get_scrolloff_value()) { + if (VIsual_active && is_drag && get_scrolloff_value(curwin)) { // In the very first line, allow scrolling one line if (mouse_row == 0) { mouse_dragging = 2; @@ -4135,7 +4135,7 @@ void scroll_redraw(int up, long count) scrollup(count, true) : scrolldown(count, true); - if (get_scrolloff_value()) { + if (get_scrolloff_value(curwin)) { // Adjust the cursor position for 'scrolloff'. Mark w_topline as // valid, otherwise the screen jumps back at the end of the file. cursor_correct(); @@ -4185,7 +4185,7 @@ static void nv_zet(cmdarg_T *cap) int old_fen = curwin->w_p_fen; bool undo = false; - int l_p_siso = (int)get_sidescrolloff_value(); + int l_p_siso = (int)get_sidescrolloff_value(curwin); assert(l_p_siso <= INT_MAX); if (ascii_isdigit(nchar)) { @@ -4253,12 +4253,13 @@ dozet: /* "z+", "z<CR>" and "zt": put cursor at top of screen */ case '+': if (cap->count0 == 0) { - /* No count given: put cursor at the line below screen */ - validate_botline(); /* make sure w_botline is valid */ - if (curwin->w_botline > curbuf->b_ml.ml_line_count) + // No count given: put cursor at the line below screen + validate_botline(curwin); // make sure w_botline is valid + if (curwin->w_botline > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - else + } else { curwin->w_cursor.lnum = curwin->w_botline; + } } FALLTHROUGH; case NL: @@ -5053,7 +5054,7 @@ static void nv_scroll(cmdarg_T *cap) setpcmark(); if (cap->cmdchar == 'L') { - validate_botline(); /* make sure curwin->w_botline is valid */ + validate_botline(curwin); // make sure curwin->w_botline is valid curwin->w_cursor.lnum = curwin->w_botline - 1; if (cap->count1 - 1 >= curwin->w_cursor.lnum) curwin->w_cursor.lnum = 1; @@ -5074,7 +5075,7 @@ static void nv_scroll(cmdarg_T *cap) /* Don't count filler lines above the window. */ used -= diff_check_fill(curwin, curwin->w_topline) - curwin->w_topfill; - validate_botline(); // make sure w_empty_rows is valid + validate_botline(curwin); // make sure w_empty_rows is valid half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2; for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) { // Count half he number of filler lines to be "below this @@ -6653,16 +6654,15 @@ static void nv_g_cmd(cmdarg_T *cap) VIsual = curwin->w_cursor; curwin->w_cursor = tpos; check_cursor(); - update_topline(); - /* - * When called from normal "g" command: start Select mode when - * 'selectmode' contains "cmd". When called for K_SELECT, always - * start Select mode. - */ - if (cap->arg) + update_topline(curwin); + // When called from normal "g" command: start Select mode when + // 'selectmode' contains "cmd". When called for K_SELECT, always + // start Select mode. + if (cap->arg) { VIsual_select = true; - else + } else { may_start_select('c'); + } setmouse(); redraw_curbuf_later(INVERTED); showmode(); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 8fddb1b561..052b07ed44 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3554,15 +3554,21 @@ void ex_display(exarg_T *eap) int name; char_u *arg = eap->arg; int clen; + char_u type[2]; if (arg != NULL && *arg == NUL) arg = NULL; int attr = HL_ATTR(HLF_8); - /* Highlight title */ - MSG_PUTS_TITLE(_("\n--- Registers ---")); + // Highlight title + msg_puts_title(_("\nType Name Content")); for (int i = -1; i < NUM_REGISTERS && !got_int; i++) { name = get_register_name(i); + switch (get_reg_type(name, NULL)) { + case kMTLineWise: type[0] = 'l'; break; + case kMTCharWise: type[0] = 'c'; break; + default: type[0] = 'b'; break; + } if (arg != NULL && vim_strchr(arg, name) == NULL) { continue; /* did not ask for this register */ @@ -3587,11 +3593,14 @@ void ex_display(exarg_T *eap) if (yb->y_array != NULL) { msg_putchar('\n'); + msg_puts(" "); + msg_putchar(type[0]); + msg_puts(" "); msg_putchar('"'); msg_putchar(name); MSG_PUTS(" "); - int n = Columns - 6; + int n = Columns - 11; for (size_t j = 0; j < yb->y_size && n > 1; j++) { if (j) { MSG_PUTS_ATTR("^J", attr); @@ -3616,8 +3625,8 @@ void ex_display(exarg_T *eap) */ if ((p = get_last_insert()) != NULL && (arg == NULL || vim_strchr(arg, '.') != NULL) && !got_int) { - MSG_PUTS("\n\". "); - dis_msg(p, TRUE); + msg_puts("\n c \". "); + dis_msg(p, true); } /* @@ -3625,8 +3634,8 @@ void ex_display(exarg_T *eap) */ if (last_cmdline != NULL && (arg == NULL || vim_strchr(arg, ':') != NULL) && !got_int) { - MSG_PUTS("\n\": "); - dis_msg(last_cmdline, FALSE); + msg_puts("\n c \": "); + dis_msg(last_cmdline, false); } /* @@ -3634,8 +3643,8 @@ void ex_display(exarg_T *eap) */ if (curbuf->b_fname != NULL && (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) { - MSG_PUTS("\n\"% "); - dis_msg(curbuf->b_fname, FALSE); + msg_puts("\n c \"% "); + dis_msg(curbuf->b_fname, false); } /* @@ -3646,8 +3655,8 @@ void ex_display(exarg_T *eap) linenr_T dummy; if (buflist_name_nr(0, &fname, &dummy) != FAIL) { - MSG_PUTS("\n\"# "); - dis_msg(fname, FALSE); + msg_puts("\n c \"# "); + dis_msg(fname, false); } } @@ -3656,8 +3665,8 @@ void ex_display(exarg_T *eap) */ if (last_search_pat() != NULL && (arg == NULL || vim_strchr(arg, '/') != NULL) && !got_int) { - MSG_PUTS("\n\"/ "); - dis_msg(last_search_pat(), FALSE); + msg_puts("\n c \"/ "); + dis_msg(last_search_pat(), false); } /* @@ -3665,8 +3674,8 @@ void ex_display(exarg_T *eap) */ if (expr_line != NULL && (arg == NULL || vim_strchr(arg, '=') != NULL) && !got_int) { - MSG_PUTS("\n\"= "); - dis_msg(expr_line, FALSE); + msg_puts("\n c \"= "); + dis_msg(expr_line, false); } } @@ -3676,9 +3685,10 @@ void ex_display(exarg_T *eap) */ static void dis_msg( - char_u *p, - int skip_esc /* if TRUE, ignore trailing ESC */ + const char_u *p, + bool skip_esc // if true, ignore trailing ESC ) + FUNC_ATTR_NONNULL_ALL { int n; int l; diff --git a/src/nvim/option.c b/src/nvim/option.c index 7786feddf3..d43dd9ba15 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4334,6 +4334,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char buf_old[NUMBUFLEN]; char buf_new[NUMBUFLEN]; char buf_type[7]; + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", @@ -7151,20 +7152,20 @@ dict_T *get_winbuf_options(const int bufopt) /// Return the effective 'scrolloff' value for the current window, using the /// global value when appropriate. -long get_scrolloff_value(void) +long get_scrolloff_value(win_T *wp) { // Disallow scrolloff in terminal-mode. #11915 if (State & TERM_FOCUS) { return 0; } - return curwin->w_p_so < 0 ? p_so : curwin->w_p_so; + return wp->w_p_so < 0 ? p_so : wp->w_p_so; } /// Return the effective 'sidescrolloff' value for the current window, using the /// global value when appropriate. -long get_sidescrolloff_value(void) +long get_sidescrolloff_value(win_T *wp) { - return curwin->w_p_siso < 0 ? p_siso : curwin->w_p_siso; + return wp->w_p_siso < 0 ? p_siso : wp->w_p_siso; } Dictionary get_vimoption(String name, Error *err) diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 64a09dc2b4..df2bfbce34 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2313,6 +2313,7 @@ return { deny_duplicates=true, vi_def=true, secure=true, + expand=true, varname='p_shadafile', defaults={if_true={vi=""}} }, diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 551a650045..aef7ffa397 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -809,7 +809,7 @@ static int pum_set_selected(int n, int repeat) no_u_sync++; win_enter(curwin_save, true); no_u_sync--; - update_topline(); + update_topline(curwin); } // Update the screen before drawing the popup menu. diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 9b8cc9ba31..aeac6e4905 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3696,7 +3696,7 @@ void ex_copen(exarg_T *eap) curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = 0; check_cursor(); - update_topline(); // scroll to show the line + update_topline(curwin); // scroll to show the line } // Move the cursor in the quickfix window to "lnum". @@ -3710,7 +3710,7 @@ static void qf_win_goto(win_T *win, linenr_T lnum) curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; curwin->w_curswant = 0; - update_topline(); // scroll to show the line + update_topline(curwin); // scroll to show the line redraw_later(curwin, VALID); curwin->w_redr_status = true; // update ruler curwin = old_curwin; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index fb08cd2727..a2589ac431 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3724,8 +3724,7 @@ static long regtry(bt_regprog_T *prog, } else { if (reg_startzp[i] != NULL && reg_endzp[i] != NULL) re_extmatch_out->matches[i] = - vim_strnsave(reg_startzp[i], - (int)(reg_endzp[i] - reg_startzp[i])); + vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]); } } } @@ -6565,7 +6564,7 @@ static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv, if (s == NULL || rsm.sm_match->endp[i] == NULL) { s = NULL; } else { - s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s)); + s = vim_strnsave(s, rsm.sm_match->endp[i] - s); } TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; TV_LIST_ITEM_TV(li)->vval.v_string = s; @@ -7084,7 +7083,7 @@ char_u *reg_submatch(int no) if (s == NULL || rsm.sm_match->endp[no] == NULL) { retval = NULL; } else { - retval = vim_strnsave(s, (int)(rsm.sm_match->endp[no] - s)); + retval = vim_strnsave(s, rsm.sm_match->endp[no] - s); } } diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index d38719f396..8b5ee59d40 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -6480,8 +6480,7 @@ static long nfa_regtry(nfa_regprog_T *prog, if (lpos->start != NULL && lpos->end != NULL) re_extmatch_out->matches[i] = - vim_strnsave(lpos->start, - (int)(lpos->end - lpos->start)); + vim_strnsave(lpos->start, lpos->end - lpos->start); } } } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2eeeebb88d..a7fd2bfcc6 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -683,7 +683,7 @@ void conceal_check_cursor_line(void) redrawWinline(curwin, curwin->w_cursor.lnum); // Need to recompute cursor column, e.g., when starting Visual mode // without concealing. */ - curs_columns(true); + curs_columns(curwin, true); } } @@ -1698,7 +1698,7 @@ static void win_update(win_T *wp, Providers *providers) const int new_wcol = wp->w_wcol; recursive = true; curwin->w_valid &= ~VALID_TOPLINE; - update_topline(); // may invalidate w_botline again + update_topline(curwin); // may invalidate w_botline again if (old_wcol != new_wcol && (wp->w_valid & (VALID_WCOL|VALID_WROW)) @@ -2013,6 +2013,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // end-of-line int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used + bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; // saved "extra" items for when draw_state becomes WL_LINE (again) int saved_n_extra = 0; @@ -2196,7 +2197,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (wp->w_p_spell - && foldinfo.fi_lines == 0 + && !has_fold && *wp->w_s->b_p_spl != NUL && !GA_EMPTY(&wp->w_s->b_langp) && *(char **)(wp->w_s->b_langp.ga_data) != NULL) { @@ -2299,6 +2300,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // handle 'incsearch' and ":s///c" highlighting } else if (highlight_match && wp == curwin + && !has_fold && lnum >= curwin->w_cursor.lnum && lnum <= curwin->w_cursor.lnum + search_match_lines) { if (lnum == curwin->w_cursor.lnum) { @@ -2551,7 +2553,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, cur = wp->w_match_head; shl_flag = false; while ((cur != NULL || !shl_flag) && !number_only - && foldinfo.fi_lines == 0 + && !has_fold ) { if (!shl_flag) { shl = &search_hl; @@ -3883,8 +3885,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, /* check if line ends before left margin */ if (vcol < v + col - win_col_off(wp)) vcol = v + col - win_col_off(wp); - /* Get rid of the boguscols now, we want to draw until the right - * edge for 'cursorcolumn'. */ + // Get rid of the boguscols now, we want to draw until the right + // edge for 'cursorcolumn'. col -= boguscols; // boguscols = 0; // Disabled because value never read after this @@ -7380,7 +7382,7 @@ void screen_resize(int width, int height) cmdline_pum_display(false); } } else { - update_topline(); + update_topline(curwin); if (pum_drawn()) { // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first. // For now make sure the nested update_screen(0) won't redraw the diff --git a/src/nvim/sign.c b/src/nvim/sign.c index ffe51287c5..fc9f53c192 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -173,13 +173,15 @@ static void insert_sign( const char_u *group, // sign group; NULL for global group int prio, // sign priority linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding + int typenr, // typenr of sign we are adding + bool has_text_or_icon // sign has text or icon ) { signlist_T *newsign = xmalloc(sizeof(signlist_T)); newsign->id = id; newsign->lnum = lnum; newsign->typenr = typenr; + newsign->has_text_or_icon = has_text_or_icon; if (group != NULL) { newsign->group = sign_group_ref(group); } else { @@ -210,13 +212,14 @@ static void insert_sign( /// Insert a new sign sorted by line number and sign priority. static void insert_sign_by_lnum_prio( - buf_T *buf, // buffer to store sign in - signlist_T *prev, // previous sign entry - int id, // sign ID - const char_u *group, // sign group; NULL for global group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding + buf_T *buf, // buffer to store sign in + signlist_T *prev, // previous sign entry + int id, // sign ID + const char_u *group, // sign group; NULL for global group + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr, // typenr of sign we are adding + bool has_text_or_icon // sign has text or icon ) { signlist_T *sign; @@ -234,7 +237,7 @@ static void insert_sign_by_lnum_prio( sign = prev->next; } - insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); + insert_sign(buf, prev, sign, id, group, prio, lnum, typenr, has_text_or_icon); } /// Get the name of a sign by its typenr. @@ -342,12 +345,13 @@ static void sign_sort_by_prio_on_line(buf_T *buf, signlist_T *sign) /// Add the sign into the signlist. Find the right spot to do it though. void buf_addsign( - buf_T *buf, // buffer to store sign in - int id, // sign ID + buf_T *buf, // buffer to store sign in + int id, // sign ID const char_u *groupname, // sign group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr, // typenr of sign we are adding + bool has_text_or_icon // sign has text or icon ) { signlist_T *sign; // a sign in the signlist @@ -363,13 +367,29 @@ void buf_addsign( sign_sort_by_prio_on_line(buf, sign); return; } else if (lnum < sign->lnum) { - insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); + insert_sign_by_lnum_prio( + buf, + prev, + id, + groupname, + prio, + lnum, + typenr, + has_text_or_icon); return; } prev = sign; } - insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); + insert_sign_by_lnum_prio( + buf, + prev, + id, + groupname, + prio, + lnum, + typenr, + has_text_or_icon); } // For an existing, placed sign "markId" change the type to "typenr". @@ -786,11 +806,15 @@ static int sign_define_init_text(sign_T *sp, char_u *text) } cells += utf_ptr2cells(s); } - // Currently must be one or two display cells - if (s != endp || cells < 1 || cells > 2) { + // Currently must be empty, one or two display cells + if (s != endp || cells > 2) { EMSG2(_("E239: Invalid sign text: %s"), text); return FAIL; } + if (cells < 1) { + sp->sn_text = NULL; + return OK; + } xfree(sp->sn_text); // Allocate one byte more if we need to pad up @@ -939,7 +963,15 @@ int sign_place( if (lnum > 0) { // ":sign place {id} line={lnum} name={name} file={fname}": // place a sign - buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr); + bool has_text_or_icon = sp->sn_text != NULL || sp->sn_icon != NULL; + buf_addsign( + buf, + *sign_id, + sign_group, + prio, + lnum, + sp->sn_typenr, + has_text_or_icon); } else { // ":sign place {id} file={fname}": change sign type lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr); diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index 687c15bbd6..c898dba890 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -1,6 +1,7 @@ #ifndef NVIM_SIGN_DEFS_H #define NVIM_SIGN_DEFS_H +#include <stdbool.h> #include "nvim/pos.h" #include "nvim/types.h" @@ -22,13 +23,14 @@ typedef struct signlist signlist_T; struct signlist { - int id; // unique identifier for each placed sign - linenr_T lnum; // line number which has this sign - int typenr; // typenr of sign - signgroup_T *group; // sign group - int priority; // priority for highlighting - signlist_T *next; // next signlist entry - signlist_T *prev; // previous entry -- for easy reordering + int id; // unique identifier for each placed sign + linenr_T lnum; // line number which has this sign + int typenr; // typenr of sign + bool has_text_or_icon; // has text or icon + signgroup_T *group; // sign group + int priority; // priority for highlighting + signlist_T *next; // next signlist entry + signlist_T *prev; // previous entry -- for easy reordering }; // Default sign priority for highlighting diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index e91d560284..4d88df5a3a 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -4186,10 +4186,10 @@ get_syn_options( arg = skiptowhite(arg); if (gname_start == arg) return NULL; - gname = vim_strnsave(gname_start, (int)(arg - gname_start)); - if (STRCMP(gname, "NONE") == 0) + gname = vim_strnsave(gname_start, arg - gname_start); + if (STRCMP(gname, "NONE") == 0) { *opt->sync_idx = NONE_IDX; - else { + } else { syn_id = syn_name2id(gname); int i; for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) @@ -4587,7 +4587,7 @@ syn_cmd_region( while (*key_end && !ascii_iswhite(*key_end) && *key_end != '=') ++key_end; xfree(key); - key = vim_strnsave_up(rest, (int)(key_end - rest)); + key = vim_strnsave_up(rest, key_end - rest); if (STRCMP(key, "MATCHGROUP") == 0) { item = ITEM_MATCHGROUP; } else if (STRCMP(key, "START") == 0) { @@ -5047,8 +5047,8 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) EMSG2(_("E401: Pattern delimiter not found: %s"), arg); return NULL; } - /* store the pattern and compiled regexp program */ - ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1)); + // store the pattern and compiled regexp program + ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1); /* Make 'cpoptions' empty, to avoid the 'l' flag */ cpo_save = p_cpo; @@ -5136,7 +5136,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) arg_end = skiptowhite(arg_start); next_arg = skipwhite(arg_end); xfree(key); - key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start)); + key = vim_strnsave_up(arg_start, arg_end - arg_start); if (STRCMP(key, "CCOMMENT") == 0) { if (!eap->skip) curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT; @@ -5195,7 +5195,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) if (!eap->skip) { /* store the pattern and compiled regexp program */ curwin->w_s->b_syn_linecont_pat = - vim_strnsave(next_arg + 1, (int)(arg_end - next_arg - 1)); + vim_strnsave(next_arg + 1, arg_end - next_arg - 1); curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; /* Make 'cpoptions' empty, to avoid the 'l' flag */ @@ -5555,18 +5555,17 @@ void ex_syntax(exarg_T *eap) { char_u *arg = eap->arg; char_u *subcmd_end; - char_u *subcmd_name; - int i; syn_cmdlinep = eap->cmdlinep; - /* isolate subcommand name */ - for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end) - ; - subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg)); - if (eap->skip) /* skip error messages for all subcommands */ - ++emsg_skip; - for (i = 0;; ++i) { + // isolate subcommand name + for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) { + } + char_u *const subcmd_name = vim_strnsave(arg, subcmd_end - arg); + if (eap->skip) { // skip error messages for all subcommands + emsg_skip++; + } + for (int i = 0;; i++) { if (subcommands[i].name == NULL) { EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name); break; @@ -6719,7 +6718,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } xfree(key); key = (char *)vim_strnsave_up((const char_u *)key_start, - (int)(linep - key_start)); + linep - key_start); linep = (const char *)skipwhite((const char_u *)linep); if (strcmp(key, "NONE") == 0) { diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index 7f6b7dcfec..24d3959f83 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -12,6 +12,9 @@ endfunc " Command to check for the presence of a working option. command -nargs=1 CheckOption call CheckOption(<f-args>) func CheckOption(name) + if !exists('&' .. a:name) + throw 'Checking for non-existent option ' .. a:name + endif if !exists('+' .. a:name) throw 'Skipped: ' .. a:name .. ' option not supported' endif @@ -74,6 +77,14 @@ func CheckCanRunGui() endif endfunc +" Command to check that we are using the GUI +command CheckGui call CheckGui() +func CheckGui() + if !has('gui_running') + throw 'Skipped: only works in the GUI' + endif +endfunc + " Command to check that not currently using the GUI command CheckNotGui call CheckNotGui() func CheckNotGui() diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index 3377f86126..53704bd094 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -61,6 +61,15 @@ func Test_client_server() call assert_fails('call remote_send("XXX", ":let testvar = ''yes''\<CR>")', 'E241') + call writefile(['one'], 'Xclientfile') + let cmd = GetVimProg() .. ' --servername ' .. name .. ' --remote Xclientfile' + call system(cmd) + call WaitForAssert({-> assert_equal('Xclientfile', remote_expr(name, "bufname()", "", 2))}) + call WaitForAssert({-> assert_equal('one', remote_expr(name, "getline(1)", "", 2))}) + call writefile(['one', 'two'], 'Xclientfile') + call system(cmd) + call WaitForAssert({-> assert_equal('two', remote_expr(name, "getline(2)", "", 2))}) + " Expression evaluated locally. if v:servername == '' call remote_startserver('MYSELF') diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 81f653c393..39f865144a 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -51,6 +51,22 @@ func Test_complete_wildmenu() call feedkeys(":e Xdir1/\<Tab>\<Down>\<Up>\<Right>\<CR>", 'tx') call assert_equal('testfile1', getline(1)) + " this fails in some Unix GUIs, not sure why + if !has('unix') || !has('gui_running') + " <C-J>/<C-K> mappings to go up/down directories when 'wildcharm' is + " different than 'wildchar'. + set wildcharm=<C-Z> + cnoremap <C-J> <Down><C-Z> + cnoremap <C-K> <Up><C-Z> + call feedkeys(":e Xdir1/\<Tab>\<C-J>\<CR>", 'tx') + call assert_equal('testfile3', getline(1)) + call feedkeys(":e Xdir1/\<Tab>\<C-J>\<C-K>\<CR>", 'tx') + call assert_equal('testfile1', getline(1)) + set wildcharm=0 + cunmap <C-J> + cunmap <C-K> + endif + " cleanup %bwipe call delete('Xdir1/Xdir2/Xtestfile4') @@ -62,6 +78,33 @@ func Test_complete_wildmenu() set nowildmenu endfunc +func Test_wildmenu_screendump() + CheckScreendump + + let lines =<< trim [SCRIPT] + set wildmenu hlsearch + [SCRIPT] + call writefile(lines, 'XTest_wildmenu') + + let buf = RunVimInTerminal('-S XTest_wildmenu', {'rows': 8}) + call term_sendkeys(buf, ":vim\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_1', {}) + + call term_sendkeys(buf, "\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_2', {}) + + call term_sendkeys(buf, "\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_3', {}) + + call term_sendkeys(buf, "\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_4', {}) + call term_sendkeys(buf, "\<Esc>") + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_wildmenu') +endfunc + func Test_map_completion() if !has('cmdline_compl') return @@ -548,6 +591,13 @@ func Test_cmdline_complete_user_names() endif endfunc +func Test_cmdline_complete_bang() + if executable('whoami') + call feedkeys(":!whoam\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('^".*\<whoami\>', @:) + endif +endfunc + funct Test_cmdline_complete_languages() let lang = substitute(execute('language messages'), '.*"\(.*\)"$', '\1', '') @@ -570,6 +620,17 @@ funct Test_cmdline_complete_languages() endif endfunc +func Test_cmdline_complete_expression() + let g:SomeVar = 'blah' + for cmd in ['exe', 'echo', 'echon', 'echomsg'] + call feedkeys(":" .. cmd .. " SomeV\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('"' .. cmd .. ' SomeVar', @:) + call feedkeys(":" .. cmd .. " foo SomeV\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('"' .. cmd .. ' foo SomeVar', @:) + endfor + unlet g:SomeVar +endfunc + func Test_cmdline_write_alternatefile() new call setline('.', ['one', 'two']) @@ -819,6 +880,36 @@ func Test_cmdwin_cedit() delfunc CmdWinType endfunc +func Test_cmdwin_restore() + CheckScreendump + + let lines =<< trim [SCRIPT] + call setline(1, range(30)) + 2split + [SCRIPT] + call writefile(lines, 'XTest_restore') + + let buf = RunVimInTerminal('-S XTest_restore', {'rows': 12}) + call term_wait(buf, 100) + call term_sendkeys(buf, "q:") + call VerifyScreenDump(buf, 'Test_cmdwin_restore_1', {}) + + " normal restore + call term_sendkeys(buf, ":q\<CR>") + call VerifyScreenDump(buf, 'Test_cmdwin_restore_2', {}) + + " restore after setting 'lines' with one window + call term_sendkeys(buf, ":close\<CR>") + call term_sendkeys(buf, "q:") + call term_sendkeys(buf, ":set lines=18\<CR>") + call term_sendkeys(buf, ":q\<CR>") + call VerifyScreenDump(buf, 'Test_cmdwin_restore_3', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_restore') +endfunc + func Test_buffers_lastused() " check that buffers are sorted by time when wildmode has lastused edit bufc " oldest @@ -894,5 +985,23 @@ func Test_zero_line_search() q! endfunc +func Test_read_shellcmd() + CheckUnix + if executable('ls') + " There should be ls in the $PATH + call feedkeys(":r! l\<c-a>\<c-b>\"\<cr>", 'tx') + call assert_match('^"r! .*\<ls\>', @:) + endif + + if executable('rm') + call feedkeys(":r! ++enc=utf-8 r\<c-a>\<c-b>\"\<cr>", 'tx') + call assert_notmatch('^"r!.*\<runtest.vim\>', @:) + call assert_match('^"r!.*\<rm\>', @:) + + call feedkeys(":r ++enc=utf-8 !rm\<c-a>\<c-b>\"\<cr>", 'tx') + call assert_notmatch('^"r.*\<runtest.vim\>', @:) + call assert_match('^"r ++enc\S\+ !.*\<rm\>', @:) + endif +endfunc -" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index 9eea27740d..b6d9687560 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -1,8 +1,8 @@ " Tests for digraphs -if !has("digraphs") - finish -endif +source check.vim +CheckFeature digraphs +source term_util.vim func Put_Dig(chars) exe "norm! o\<c-k>".a:chars @@ -487,4 +487,20 @@ func Test_show_digraph_cp1251() bwipe! endfunc +" Test for the characters displayed on the screen when entering a digraph +func Test_entering_digraph() + CheckRunVimInTerminal + let buf = RunVimInTerminal('', {'rows': 6}) + call term_sendkeys(buf, "i\<C-K>") + call term_wait(buf) + call assert_equal('?', term_getline(buf, 1)) + call term_sendkeys(buf, "1") + call term_wait(buf) + call assert_equal('1', term_getline(buf, 1)) + call term_sendkeys(buf, "2") + call term_wait(buf) + call assert_equal('½', term_getline(buf, 1)) + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 356a7ce135..2123780f11 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -181,6 +181,7 @@ let s:filename_checks = { \ 'gdb': ['.gdbinit'], \ 'gdmo': ['file.mo', 'file.gdmo'], \ 'gedcom': ['file.ged', 'lltxxxxx.txt'], + \ 'gift': ['file.gift'], \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'], \ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig'], \ 'gitolite': ['gitolite.conf'], diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index bdeaf8e2cf..7ccf2812ff 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -1,3 +1,4 @@ +source check.vim func Test_yank_put_clipboard() new @@ -10,6 +11,16 @@ func Test_yank_put_clipboard() bwipe! endfunc +func Test_global_set_clipboard() + CheckFeature clipboard_working + new + set clipboard=unnamedplus + let @+='clipboard' | g/^/set cb= | let @" = 'unnamed' | put + call assert_equal(['','unnamed'], getline(1, '$')) + set clipboard& + bwipe! +endfunc + func Test_nested_global() new call setline(1, ['nothing', 'found', 'found bad', 'bad']) diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 8ef8bbc23a..f71da92bf8 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -178,15 +178,20 @@ func Test_mksession_rtp() set sessionoptions& endfunc -" Verify that arglist is stored correctly to the session file. func Test_mksession_arglist() %argdel next file1 file2 file3 file4 + new + next | next mksession! Xtest_mks.out source Xtest_mks.out call assert_equal(['file1', 'file2', 'file3', 'file4'], argv()) + call assert_equal(2, argidx()) + wincmd w + call assert_equal(0, argidx()) call delete('Xtest_mks.out') + enew | only argdel * endfunc @@ -220,6 +225,53 @@ func Test_mksession_one_buffer_two_windows() call delete('Xtest_mks.out') endfunc +func Test_mksession_lcd_multiple_tabs() + tabnew + tabnew + lcd . + tabfirst + lcd . + mksession! Xtest_mks.out + tabonly + source Xtest_mks.out + call assert_true(haslocaldir(), 'Tab 1 localdir') + tabnext 2 + call assert_true(!haslocaldir(), 'Tab 2 localdir') + tabnext 3 + call assert_true(haslocaldir(), 'Tab 3 localdir') + call delete('Xtest_mks.out') +endfunc + +func Test_mksession_blank_tabs() + tabnew + tabnew + tabnew + tabnext 3 + mksession! Xtest_mks.out + tabnew + tabnew + tabnext 2 + source Xtest_mks.out + call assert_equal(4, tabpagenr('$'), 'session restore should restore number of tabs') + call assert_equal(3, tabpagenr(), 'session restore should restore the active tab') + call delete('Xtest_mks.out') +endfunc + +func Test_mksession_blank_windows() + split + split + split + 3 wincmd w + mksession! Xtest_mks.out + split + split + 2 wincmd w + source Xtest_mks.out + call assert_equal(4, winnr('$'), 'session restore should restore number of windows') + call assert_equal(3, winnr(), 'session restore should restore the active window') + call delete('Xtest_mks.out') +endfunc + if has('extra_search') func Test_mksession_hlsearch() @@ -356,24 +408,28 @@ func Test_mksession_globals() set sessionoptions+=globals " create different global variables - let g:Global_string = "Sun is shining" + let g:Global_string = "Sun is shining\r\n" let g:Global_count = 100 let g:Global_pi = 3.14 + let g:Global_neg_float = -2.68 mksession! Xtest_mks.out unlet g:Global_string unlet g:Global_count unlet g:Global_pi + unlet g:Global_neg_float source Xtest_mks.out - call assert_equal("Sun is shining", g:Global_string) + call assert_equal("Sun is shining\r\n", g:Global_string) call assert_equal(100, g:Global_count) call assert_equal(3.14, g:Global_pi) + call assert_equal(-2.68, g:Global_neg_float) unlet g:Global_string unlet g:Global_count unlet g:Global_pi + unlet g:Global_neg_float call delete('Xtest_mks.out') set sessionoptions& endfunc @@ -432,20 +488,123 @@ func Test_mksession_resize() for line in lines if line =~ '^set lines=' let found_resize = v:true + break endif endfor - call assert_equal(v:false, found_resize) + call assert_false(found_resize) let lines = readfile('Xtest_mks2.out') let found_resize = v:false for line in lines if line =~ '^set lines=' let found_resize = v:true + break + endif + endfor + call assert_true(found_resize) + + call delete('Xtest_mks1.out') + call delete('Xtest_mks2.out') + set sessionoptions& +endfunc + +" Test for mksession with a named scratch buffer +func Test_mksession_scratch() + set sessionoptions&vi + enew | only + file Xscratch + set buftype=nofile + mksession! Xtest_mks.out + %bwipe + source Xtest_mks.out + call assert_equal('Xscratch', bufname('')) + call assert_equal('nofile', &buftype) + %bwipe + call delete('Xtest_mks.out') + set sessionoptions& +endfunc + +" Test for mksession with fold options +func Test_mksession_foldopt() + set sessionoptions-=options + set sessionoptions+=folds + new + setlocal foldenable + setlocal foldmethod=expr + setlocal foldmarker=<<<,>>> + setlocal foldignore=% + setlocal foldlevel=2 + setlocal foldminlines=10 + setlocal foldnestmax=15 + mksession! Xtest_mks.out + close + %bwipe + + source Xtest_mks.out + call assert_true(&foldenable) + call assert_equal('expr', &foldmethod) + call assert_equal('<<<,>>>', &foldmarker) + call assert_equal('%', &foldignore) + call assert_equal(2, &foldlevel) + call assert_equal(10, &foldminlines) + call assert_equal(15, &foldnestmax) + + close + %bwipe + set sessionoptions& +endfunc + +" Test for mksession with window position +func Test_mksession_winpos() + if !has('gui_running') + " Only applicable in GUI Vim + return + endif + set sessionoptions+=winpos + mksession! Xtest_mks.out + let found_winpos = v:false + let lines = readfile('Xtest_mks.out') + for line in lines + if line =~ '^winpos ' + let found_winpos = v:true + break + endif + endfor + call assert_true(found_winpos) + call delete('Xtest_mks.out') + set sessionoptions& +endfunc + +" Test for mksession with 'compatible' option +func Test_mksession_compatible() + throw 'skipped: Nvim does not support "compatible" option' + mksession! Xtest_mks1.out + set compatible + mksession! Xtest_mks2.out + set nocp + + let test_success = v:false + let lines = readfile('Xtest_mks1.out') + for line in lines + if line =~ '^if &cp | set nocp | endif' + let test_success = v:true + break + endif + endfor + call assert_true(test_success) + + let test_success = v:false + let lines = readfile('Xtest_mks2.out') + for line in lines + if line =~ '^if !&cp | set cp | endif' + let test_success = v:true + break endif endfor - call assert_equal(v:true, found_resize) + call assert_true(test_success) call delete('Xtest_mks1.out') call delete('Xtest_mks2.out') + set compatible& set sessionoptions& endfunc diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 24e02cb385..8d2a768ba0 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -2,6 +2,9 @@ " Tests for register operations " +source check.vim +source view_util.vim + " This test must be executed first to check for empty and unset registers. func Test_aaa_empty_reg_test() call assert_fails('normal @@', 'E748:') @@ -52,31 +55,44 @@ func Test_display_registers() let b = execute('registers') call assert_equal(a, b) - call assert_match('^\n--- Registers ---\n' - \ . '"" a\n' - \ . '"0 ba\n' - \ . '"1 b\n' - \ . '"a b\n' + call assert_match('^\nType Name Content\n' + \ . ' c "" a\n' + \ . ' c "0 ba\n' + \ . ' c "1 b\n' + \ . ' c "a b\n' \ . '.*' - \ . '"- a\n' + \ . ' c "- a\n' \ . '.*' - \ . '": ls\n' - \ . '"% file2\n' - \ . '"# file1\n' - \ . '"/ bar\n' - \ . '"= 2\*4', a) + \ . ' c ": ls\n' + \ . ' c "% file2\n' + \ . ' c "# file1\n' + \ . ' c "/ bar\n' + \ . ' c "= 2\*4', a) let a = execute('registers a') - call assert_match('^\n--- Registers ---\n' - \ . '"a b', a) + call assert_match('^\nType Name Content\n' + \ . ' c "a b', a) let a = execute('registers :') - call assert_match('^\n--- Registers ---\n' - \ . '": ls', a) + call assert_match('^\nType Name Content\n' + \ . ' c ": ls', a) bwipe! endfunc +func Test_recording_status_in_ex_line() + norm qx + redraw! + call assert_equal('recording @x', Screenline(&lines)) + set shortmess=q + redraw! + call assert_equal('recording', Screenline(&lines)) + set shortmess& + norm q + redraw! + call assert_equal('', Screenline(&lines)) +endfunc + " Check that replaying a typed sequence does not use an Esc and following " characters as an escape sequence. func Test_recording_esc_sequence() @@ -259,9 +275,9 @@ func Test_insert_small_delete() call setline(1, ['foo foobar bar']) call cursor(1,1) exe ":norm! ciw'\<C-R>-'" - call assert_equal(getline(1), "'foo' foobar bar") + call assert_equal("'foo' foobar bar", getline(1)) exe ":norm! w.w." - call assert_equal(getline(1), "'foo' 'foobar' 'bar'") + call assert_equal("'foo' 'foobar' 'bar'", getline(1)) bwipe! endfunc diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 1e6c311374..4bbd722fdb 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -122,9 +122,9 @@ func Test_sign() call assert_fails("sign define Sign4 text=a\e linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text=\ea linehl=Comment", 'E239:') - " Only 1 or 2 character text is allowed + " Only 0, 1 or 2 character text is allowed call assert_fails("sign define Sign4 text=abc linehl=Comment", 'E239:') - call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') + " call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:') " define sign with whitespace @@ -306,7 +306,7 @@ func Test_sign_invalid_commands() call assert_fails('sign jump 1 name=', 'E474:') call assert_fails('sign jump 1 name=Sign1', 'E474:') call assert_fails('sign jump 1 line=100', '474:') - call assert_fails('sign define Sign2 text=', 'E239:') + " call assert_fails('sign define Sign2 text=', 'E239:') " Non-numeric identifier for :sign place call assert_fails("sign place abc line=3 name=Sign1 buffer=" . bufnr(''), \ 'E474:') @@ -415,7 +415,7 @@ func Test_sign_funcs() " Tests for invalid arguments to sign_define() call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:') - call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:') + " call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:') call assert_fails('call sign_define([])', 'E730:') call assert_fails('call sign_define("sign6", [])', 'E715:') diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index bc7c69d920..2b6a89647e 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -1,6 +1,7 @@ " Tests for tabpage source screendump.vim +source check.vim function Test_tabpage() bw! @@ -222,6 +223,34 @@ function Test_tabpage_with_autocmd() 1tabonly! endfunction +" Test autocommands on tab drop +function Test_tabpage_with_autocmd_tab_drop() + augroup TestTabpageGroup + au! + autocmd TabEnter * call add(s:li, 'TabEnter') + autocmd WinEnter * call add(s:li, 'WinEnter') + autocmd BufEnter * call add(s:li, 'BufEnter') + autocmd TabLeave * call add(s:li, 'TabLeave') + autocmd WinLeave * call add(s:li, 'WinLeave') + autocmd BufLeave * call add(s:li, 'BufLeave') + augroup END + + let s:li = [] + tab drop test1 + call assert_equal(['BufLeave', 'BufEnter'], s:li) + + let s:li = [] + tab drop test2 test3 + call assert_equal([ + \ 'TabLeave', 'TabEnter', 'TabLeave', 'TabEnter', + \ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter', + \ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter'], s:li) + + autocmd! TestTabpageGroup + augroup! TestTabpageGroup + 1tabonly! +endfunction + function Test_tabpage_with_tab_modifier() for n in range(4) tabedit @@ -579,4 +608,82 @@ func Test_tabpage_cmdheight() call delete('XTest_tabpage_cmdheight') endfunc +" Return the terminal key code for selecting a tab page from the tabline. This +" sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0), +" KS_FILLER (0x58) and then the tab page number. +func TabLineSelectPageCode(tabnr) + return "\x9b\xf0\x58" .. nr2char(a:tabnr) +endfunc + +" Return the terminal key code for opening a new tabpage from the tabpage +" menu. This sequence consists of the following codes: a CSI (0x9b), +" KS_TABMENU (0xef), KS_FILLER (0x58), the tab page number and +" TABLINE_MENU_NEW (2). +func TabMenuNewItemCode(tabnr) + return "\x9b\xef\x58" .. nr2char(a:tabnr) .. nr2char(2) +endfunc + +" Return the terminal key code for closing a tabpage from the tabpage menu. +" This sequence consists of the following codes: a CSI (0x9b), KS_TABMENU +" (0xef), KS_FILLER (0x58), the tab page number and TABLINE_MENU_CLOSE (1). +func TabMenuCloseItemCode(tabnr) + return "\x9b\xef\x58" .. nr2char(a:tabnr) .. nr2char(1) +endfunc + +" Test for using the tabpage menu from the insert and normal modes +func Test_tabline_tabmenu() + " only works in GUI + CheckGui + + %bw! + tabnew + tabnew + call assert_equal(3, tabpagenr()) + + " go to tab page 2 in normal mode + call feedkeys(TabLineSelectPageCode(2), "Lx!") + call assert_equal(2, tabpagenr()) + + " close tab page 3 in normal mode + call feedkeys(TabMenuCloseItemCode(3), "Lx!") + call assert_equal(2, tabpagenr('$')) + call assert_equal(2, tabpagenr()) + + " open new tab page before tab page 1 in normal mode + call feedkeys(TabMenuNewItemCode(1), "Lx!") + call assert_equal(1, tabpagenr()) + call assert_equal(3, tabpagenr('$')) + + " go to tab page 2 in operator-pending mode (should beep) + call assert_beeps('call feedkeys("f" .. TabLineSelectPageCode(2), "Lx!")') + + " open new tab page before tab page 1 in operator-pending mode (should beep) + call assert_beeps('call feedkeys("f" .. TabMenuNewItemCode(1), "Lx!")') + + " open new tab page after tab page 3 in normal mode + call feedkeys(TabMenuNewItemCode(4), "Lx!") + call assert_equal(4, tabpagenr()) + call assert_equal(4, tabpagenr('$')) + + " go to tab page 2 in insert mode + call feedkeys("i" .. TabLineSelectPageCode(2) .. "\<C-C>", "Lx!") + call assert_equal(2, tabpagenr()) + + " close tab page 2 in insert mode + call feedkeys("i" .. TabMenuCloseItemCode(2) .. "\<C-C>", "Lx!") + call assert_equal(3, tabpagenr('$')) + + " open new tab page before tab page 3 in insert mode + call feedkeys("i" .. TabMenuNewItemCode(3) .. "\<C-C>", "Lx!") + call assert_equal(3, tabpagenr()) + call assert_equal(4, tabpagenr('$')) + + " open new tab page after tab page 4 in insert mode + call feedkeys("i" .. TabMenuNewItemCode(5) .. "\<C-C>", "Lx!") + call assert_equal(5, tabpagenr()) + call assert_equal(5, tabpagenr('$')) + + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index c630e678fd..bed39d0741 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -850,6 +850,14 @@ func Test_window_resize() exe other_winnr .. 'resize +1' call assert_equal(12, winheight(other_winnr)) call assert_equal(&lines - 10 - 3 -2, winheight(0)) + close + + vsplit + wincmd l + let other_winnr = winnr('h') + call assert_notequal(winnr(), other_winnr) + exe 'vert ' .. other_winnr .. 'resize -100' + call assert_equal(0, winwidth(other_winnr)) %bwipe! endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index ad1e1f8f02..00f49724b6 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4540,7 +4540,7 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, // Might need to scroll the old window before switching, e.g., when the // cursor was moved. - update_topline(); + update_topline(curwin); // may have to copy the buffer options when 'cpo' contains 'S' if (wp->w_buffer != curbuf) { @@ -5013,7 +5013,10 @@ void win_size_save(garray_T *gap) { ga_init(gap, (int)sizeof(int), 1); - ga_grow(gap, win_count() * 2); + ga_grow(gap, win_count() * 2 + 1); + // first entry is value of 'lines' + ((int *)gap->ga_data)[gap->ga_len++] = Rows; + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { ((int *)gap->ga_data)[gap->ga_len++] = wp->w_width + wp->w_vsep_width; @@ -5021,18 +5024,18 @@ void win_size_save(garray_T *gap) } } -/* - * Restore window sizes, but only if the number of windows is still the same. - * Does not free the growarray. - */ +// Restore window sizes, but only if the number of windows is still the same +// and 'lines' didn't change. +// Does not free the growarray. void win_size_restore(garray_T *gap) + FUNC_ATTR_NONNULL_ALL { - if (win_count() * 2 == gap->ga_len) { - /* The order matters, because frames contain other frames, but it's - * difficult to get right. The easy way out is to do it twice. */ - for (int j = 0; j < 2; ++j) - { - int i = 0; + if (win_count() * 2 + 1 == gap->ga_len + && ((int *)gap->ga_data)[0] == Rows) { + // The order matters, because frames contain other frames, but it's + // difficult to get right. The easy way out is to do it twice. + for (int j = 0; j < 2; j++) { + int i = 1; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { int width = ((int *)gap->ga_data)[i++]; int height = ((int *)gap->ga_data)[i++]; @@ -5346,6 +5349,8 @@ void win_setwidth_win(int width, win_T *wp) width = p_wmw; if (width == 0) width = 1; + } else if (width < 0) { + width = 0; } if (wp->w_floating) { wp->w_float_config.width = width; @@ -5873,10 +5878,10 @@ void scroll_to_fraction(win_T *wp, int prev_height) } if (wp == curwin) { - if (get_scrolloff_value()) { - update_topline(); + if (get_scrolloff_value(wp)) { + update_topline(wp); } - curs_columns(false); // validate w_wrow + curs_columns(wp, false); // validate w_wrow } if (prev_height > 0) { wp->w_prev_fraction_row = wp->w_wrow; @@ -5932,8 +5937,8 @@ void win_set_inner_size(win_T *wp) changed_line_abv_curs_win(wp); invalidate_botline_win(wp); if (wp == curwin) { - update_topline(); - curs_columns(true); // validate w_wrow + update_topline(wp); + curs_columns(wp, true); // validate w_wrow } redraw_later(wp, NOT_VALID); } diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 29cd38ef0d..9ee2570798 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -3,6 +3,8 @@ local eq, clear, eval, command, nvim, next_msg = helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.nvim, helpers.next_msg local meths = helpers.meths +local exec_lua = helpers.exec_lua +local retry = helpers.retry describe('notify', function() local channel @@ -72,4 +74,18 @@ describe('notify', function() nvim('unsubscribe', 'event1') eq(2, eval('1+1')) -- Still alive? end) + + it('cancels stale events on channel close', function() + if helpers.pending_win32(pending) then return end + local catchan = eval("jobstart(['cat'], {'rpc': v:true})") + eq({id=catchan, stream='job', mode='rpc', client = {}}, exec_lua ([[ + vim.rpcnotify(..., "nvim_call_function", 'chanclose', {..., 'rpc'}) + vim.rpcnotify(..., "nvim_subscribe", "daily_rant") + return vim.api.nvim_get_chan_info(...) + ]], catchan)) + eq(2, eval('1+1')) -- Still alive? + eq({false, 'Invalid channel: '..catchan}, + exec_lua ([[ return {pcall(vim.rpcrequest, ..., 'nvim_eval', '1+1')}]], catchan)) + retry(nil, 3000, function() eq({}, meths.get_chan_info(catchan)) end) -- cat be dead :( + end) end) diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index 1431054494..2c681eb9d8 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -605,10 +605,10 @@ describe('clipboard (with fake clipboard.vim)', function() {0:~ }| {4: }| :registers | - {1:--- Registers ---} | - "* some{2:^J}star data{2:^J} | - "+ such{2:^J}plus{2:^J}stuff | - ": let g:test_clip['+'] = ['such', 'plus', 'stuff'] | + {1:Type Name Content} | + l "* some{2:^J}star data{2:^J} | + c "+ such{2:^J}plus{2:^J}stuff | + c ": let g:test_clip['+'] = ['such', 'plus', 'stuff'] | {3:Press ENTER or type command to continue}^ | ]], { [0] = {bold = true, foreground = Screen.colors.Blue}, diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 222275eb4d..656f613c6a 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -20,6 +20,7 @@ describe('search highlighting', function() [2] = {background = colors.Yellow}, -- Search [3] = {reverse = true}, [4] = {foreground = colors.Red}, -- Message + [6] = {foreground = Screen.colors.Blue4, background = Screen.colors.LightGrey}, -- Folded }) end) @@ -38,6 +39,21 @@ describe('search highlighting', function() ]]) end) + it('is disabled in folded text', function() + insert("some text\nmore text") + feed_command('1,2fold') + feed("gg/text") + screen:expect([[ + {6:+-- 2 lines: some text·················}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + /text^ | + ]]) + end) + it('works', function() insert([[ some text @@ -455,6 +471,19 @@ describe('search highlighting', function() {4:search hit BOTTOM, continuing at TOP} | ]]) + -- check hilights work also in folds + feed("zf4j") + command("%foldopen") + screen:expect([[ + very {5:spec^ial}{2: te}{6:xt} | + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:search hit BOTTOM, continuing at TOP} | + ]]) + feed_command("call clearmatches()") screen:expect([[ very spec{2:^ial te}xt | diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index 0ed62b21b2..d1b8de5e4e 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -76,6 +76,28 @@ describe('Signs', function() ]]) end) + it('allows signs with no text', function() + feed('ia<cr>b<cr><esc>') + command('sign define piet1 text= texthl=Search') + command('sign place 1 line=1 name=piet1 buffer=1') + screen:expect([[ + a | + b | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) + it('can be called right after :split', function() feed('ia<cr>b<cr>c<cr><esc>gg') -- This used to cause a crash due to :sign using a special redraw @@ -244,6 +266,50 @@ describe('Signs', function() ]]} end) + it('ignores signs with no icon and text when calculting the signcolumn width', function() + feed('ia<cr>b<cr>c<cr><esc>') + command('set number') + command('set signcolumn=auto:2') + command('sign define pietSearch text=>> texthl=Search') + command('sign define pietError text= texthl=Error') + command('sign place 2 line=1 name=pietError buffer=1') + -- no signcolumn with only empty sign + screen:expect([[ + {6: 1 }a | + {6: 2 }b | + {6: 3 }c | + {6: 4 }^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + -- single column with 1 sign with text and one sign without + command('sign place 1 line=1 name=pietSearch buffer=1') + screen:expect([[ + {1:>>}{6: 1 }a | + {2: }{6: 2 }b | + {2: }{6: 3 }c | + {2: }{6: ^4 } | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) + it('can have 32bit sign IDs', function() command('sign define piet text=>> texthl=Search') command('sign place 100000 line=1 name=piet buffer=1') |