diff options
211 files changed, 11351 insertions, 6401 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70cdc3b4a2..3653f04d5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,15 @@ name: CI -on: [push, pull_request] +on: + push: + branches: '**' + pull_request: + branches: 'master' jobs: - build: + unixish: name: ${{ matrix.os }} ${{ matrix.flavor }} (cc=${{ matrix.cc }}) strategy: + fail-fast: false matrix: include: - flavor: asan @@ -51,6 +56,7 @@ jobs: - name: Install brew packages if: matrix.os == 'osx' run: | + rm -f /usr/local/bin/2to3 brew update >/dev/null brew install automake ccache cpanminus ninja brew upgrade @@ -77,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 new file mode 100644 index 0000000000..abcdd00721 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,205 @@ +name: Release +on: + schedule: + - cron: '5 5 * * *' + workflow_dispatch: + inputs: + tag_name: + description: 'Tag name for release' + required: false + default: nightly + push: + tags: + - v[0-9]+.[0-9]+.[0-9]+ + +# Build on the oldest supported images, so we have broader compatibility +jobs: + linux: + runs-on: ubuntu-16.04 + outputs: + version: ${{ steps.build.outputs.version }} + release: ${{ steps.build.outputs.release }} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake build-essential cmake gcc-multilib gettext gperf libtool-bin locales ninja-build pkg-config unzip + - name: Build release + id: build + run: | + make CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH=" + printf '::set-output name=version::%s\n' "$(./build/bin/nvim --version | head -n 3 | sed -z 's/\n/%0A/g')" + printf '::set-output name=release::%s\n' "$(./build/bin/nvim --version | head -n 1)" + make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-linux64" install + cd "$GITHUB_WORKSPACE/build/release" + tar cfz nvim-linux64.tar.gz nvim-linux64 + - uses: actions/upload-artifact@v2 + with: + name: nvim-linux64 + path: build/release/nvim-linux64.tar.gz + retention-days: 1 + + appimage: + runs-on: ubuntu-16.04 + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake build-essential cmake gcc-multilib gettext gperf libtool-bin locales ninja-build pkg-config unzip + - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') + run: make appimage-latest + - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly') + run: make appimage-nightly + - uses: actions/upload-artifact@v2 + with: + name: appimage + path: build/bin/nvim.appimage + retention-days: 1 + - uses: actions/upload-artifact@v2 + with: + name: appimage + path: build/bin/nvim.appimage.zsync + retention-days: 1 + + macOS: + runs-on: macos-10.15 + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Install brew packages + run: | + rm -f /usr/local/bin/2to3 + brew update >/dev/null + brew install automake ninja + - name: Build release + run: | + make CMAKE_BUILD_TYPE=Release CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11" + make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-osx64" install + - name: Create package + run: | + cd "$GITHUB_WORKSPACE/build/release" + mkdir -p nvim-osx64/libs + libs=($(otool -L nvim-osx64/bin/nvim | sed 1d | sed -E -e 's|^[[:space:]]*||' -e 's| .*||')) + echo "libs:" + for lib in "${libs[@]}"; do + if echo "$lib" | grep -q -E 'libSystem|CoreFoundation' 2>/dev/null; then + echo " [skipped] $lib" + else + echo " $lib" + relname="libs/${lib##*/}" + cp -L "$lib" "nvim-osx64/$relname" + install_name_tool -change "$lib" "@executable_path/../$relname" nvim-osx64/bin/nvim + fi + done + tar cfz nvim-macos.tar.gz nvim-osx64 + - uses: actions/upload-artifact@v2 + with: + name: nvim-macos + 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, windows] + runs-on: ubuntu-20.04 + steps: + - uses: actions/download-artifact@v2 + - if: github.event_name == 'workflow_dispatch' + run: echo "TAG_NAME=${{ github.event.inputs.tag_name }}" >> $GITHUB_ENV + - if: github.event_name == 'schedule' + run: echo 'TAG_NAME=nightly' >> $GITHUB_ENV + - if: github.event_name == 'push' + run: | + TAG_NAME=${{ github.ref }} + echo "TAG_NAME=${TAG_NAME#refs/tags/}" >> $GITHUB_ENV + - if: env.TAG_NAME == 'nightly' + run: echo 'SUBJECT=Nvim development (prerelease) build' >> $GITHUB_ENV + - if: env.TAG_NAME != 'nightly' + run: echo 'SUBJECT=Nvim release build' >> $GITHUB_ENV + - if: env.TAG_NAME == 'nightly' + uses: dev-drprasad/delete-tag-and-release@v0.1.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + delete_release: '' + tag_name: nightly + - uses: meeDamian/github-release@2.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ env.TAG_NAME }} + name: ${{ needs.linux.outputs.release }} + prerelease: ${{ env.TAG_NAME == 'nightly' }} + commitish: ${{ github.sha }} + gzip: false + allow_override: ${{ env.TAG_NAME == 'nightly' }} + files: | + nvim-macos.tar.gz:./nvim-macos/nvim-macos.tar.gz + 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 }} + ``` + ${{ needs.linux.outputs.version }}``` + + ## Install + + ### Windows + + 1. Extract **nvim-win64.zip** (or **nvim-win32.zip**) + 2. Run `nvim-qt.exe` + + ### macOS + + 1. Download **nvim-macos.tar.gz** + 2. Extract: `tar xzvf nvim-macos.tar.gz` + 3. Run `./nvim-osx64/bin/nvim` + + ### Linux (x64) + + 1. Download **nvim.appimage** + 2. Run `chmod u+x nvim.appimage && ./nvim.appimage` + - If your system does not have FUSE you can [extract the appimage](https://github.com/AppImage/AppImageKit/wiki/FUSE#type-2-appimage): + ``` + ./nvim.appimage --appimage-extract + ./squashfs-root/usr/bin/nvim + ``` + + ### Other + + - Install by [package manager](https://github.com/neovim/neovim/wiki/Installing-Neovim) 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 78588fb3da..e3c67c55cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -352,8 +352,8 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") add_definitions(-D_GNU_SOURCE) endif() -if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SIZEOF_VOID_P EQUAL 8) - # Required for luajit. +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT PREFER_LUA AND LUAJIT_VERSION LESS "2.1.0-beta3") + # Required for luajit < 2.1.0-beta3. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pagezero_size 10000 -image_base 100000000") set(CMAKE_SHARED_LINKER_FLAGS @@ -659,7 +659,7 @@ endif() if(LUACHECK_PRG) add_custom_target(lualint - COMMAND ${LUACHECK_PRG} -q runtime/ src/ test/ + COMMAND ${LUACHECK_PRG} -q runtime/ src/ test/ --exclude-files test/functional/fixtures/lua/syntax_error.lua WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) else() add_custom_target(lualint false @@ -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 new file mode 100644 index 0000000000..d18534215c --- /dev/null +++ b/contrib/flake.nix @@ -0,0 +1,102 @@ +{ + description = "Neovim flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + { + overlay = final: prev: + let + pkgs = nixpkgs.legacyPackages.${prev.system}; + in + rec { + neovim = pkgs.neovim-unwrapped.overrideAttrs (oa: { + version = "master"; + src = ../.; + + buildInputs = oa.buildInputs ++ ([ + pkgs.tree-sitter + ]); + + cmakeFlags = oa.cmakeFlags ++ [ + "-DUSE_BUNDLED=OFF" + ]; + }); + + # a development binary to help debug issues + neovim-debug = (neovim.override { + stdenv = if pkgs.stdenv.isLinux then pkgs.llvmPackages_latest.stdenv else pkgs.stdenv; + lua = pkgs.enableDebugging pkgs.luajit; + }).overrideAttrs (oa: { + cmakeBuildType = "Debug"; + cmakeFlags = oa.cmakeFlags ++ [ + "-DMIN_LOG_LEVEL=0" + ]; + }); + + # for neovim developers, very slow + # brings development tools as well + neovim-developer = + let + lib = nixpkgs.lib; + pythonEnv = pkgs.python3; + luacheck = pkgs.luaPackages.luacheck; + in + (neovim-debug.override ({ doCheck = pkgs.stdenv.isLinux; })).overrideAttrs (oa: { + cmakeFlags = oa.cmakeFlags ++ [ + "-DLUACHECK_PRG=${luacheck}/bin/luacheck" + "-DMIN_LOG_LEVEL=0" + "-DENABLE_LTO=OFF" + "-DUSE_BUNDLED=OFF" + ] ++ pkgs.stdenv.lib.optionals pkgs.stdenv.isLinux [ + # https://github.com/google/sanitizers/wiki/AddressSanitizerFlags + # https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports + "-DCLANG_ASAN_UBSAN=ON" + ]; + + nativeBuildInputs = oa.nativeBuildInputs ++ (with pkgs; [ + pythonEnv + include-what-you-use # for scripts/check-includes.py + jq # jq for scripts/vim-patch.sh -r + doxygen + ]); + + shellHook = oa.shellHook + '' + export NVIM_PYTHON_LOG_LEVEL=DEBUG + export NVIM_LOG_FILE=/tmp/nvim.log + + export ASAN_OPTIONS="log_path=./test.log:abort_on_error=1" + export UBSAN_OPTIONS=print_stacktrace=1 + ''; + }); + }; + } // + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + overlays = [ self.overlay ]; + inherit system; + }; + in + rec { + + packages = with pkgs; { + inherit neovim neovim-debug neovim-developer; + }; + + defaultPackage = pkgs.neovim; + + apps = { + nvim = flake-utils.lib.mkApp { drv = pkgs.neovim; name = "nvim"; }; + nvim-debug = flake-utils.lib.mkApp { drv = pkgs.neovim-debug; name = "nvim"; }; + }; + + defaultApp = apps.nvim; + + devShell = pkgs.neovim-developer; + } + ); +} diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim index f18801ea69..8b734bbb6f 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -41,10 +41,27 @@ function! s:check_config() abort \ 'Check `:verbose set paste?` to see if a plugin or script set the option.', ]) endif - let shadafile = (empty(&shadafile) || &shadafile ==# 'NONE') ? stdpath('data').'/shada/main.shada' : &shadafile - if !empty(shadafile) && (!filereadable(shadafile) || !filewritable(shadafile)) + let writeable = v:true + let shadafile = substitute(matchstr( + \ split(&shada, ',')[-1], '^n.\+'), '^n', '', '') + let shadafile = empty(&shadafile) ? empty(shadafile) ? + \ stdpath('data').'/shada/main.shada' : expand(shadafile) + \ : &shadafile ==# 'NONE' ? '' : &shadafile + if !empty(shadafile) && empty(glob(shadafile)) + " Since this may be the first time neovim has been run, we will try to + " create a shada file + try + wshada + catch /.*/ + let writeable = v:false + endtry + endif + if !writeable || (!empty(shadafile) && + \ (!filereadable(shadafile) || !filewritable(shadafile))) let ok = v:false - call health#report_error('shada file is not '.(filereadable(shadafile) ? 'writeable' : 'readable').":\n".shadafile) + call health#report_error('shada file is not '. + \ ((!writeable || filereadable(shadafile)) ? + \ 'writeable' : 'readable').":\n".shadafile) endif if ok diff --git a/runtime/autoload/javascriptcomplete.vim b/runtime/autoload/javascriptcomplete.vim index 14bc3d7ce3..29b6b16254 100644 --- a/runtime/autoload/javascriptcomplete.vim +++ b/runtime/autoload/javascriptcomplete.vim @@ -1,7 +1,9 @@ " Vim completion script " Language: Java Script -" Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl ) -" Last Change: 2017 Mar 04 +" Maintainer: Jay Sitter (jay@jaysitter.com) +" URL: https://github.com/jsit/javascriptcomplete.vim/ +" Previous Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl ) +" Last Change: 2020 Jul 30 function! javascriptcomplete#CompleteJS(findstart, base) if a:findstart @@ -154,12 +156,30 @@ function! javascriptcomplete#CompleteJS(findstart, base) \ 'text', 'vLink'] let bodys = bodyprop " Document - document. - let docuprop = ['anchors', 'applets', 'childNodes', 'embeds', 'forms', 'images', 'links', 'stylesheets', - \ 'body', 'cookie', 'documentElement', 'domain', 'lastModified', 'referrer', 'title', 'URL'] - let documeth = ['close', 'createAttribute', 'createElement', 'createTextNode', 'focus', 'getElementById', - \ 'getElementsByName', 'getElementsByTagName', 'open', 'write', 'writeln', - \ 'onClick', 'onDblClick', 'onFocus', 'onKeyDown', 'onKeyPress', 'onKeyUp', - \ 'onMouseDown', 'onMouseMove', 'onMouseOut', 'onMouseOver', 'onMouseUp', 'onResize'] + let docuprop = ['anchors', 'body', 'characterSet', 'doctype', + \ 'documentElement', 'documentURI', 'embeds', 'fonts', 'forms', + \ 'head', 'hidden', 'images', 'implementation', 'lastStyleSheetSet', + \ 'links', 'plugins', 'preferredStyleSheetSet', 'scripts', + \ 'scrollingElement', 'selectedStyleSheetSet', 'styleSheetSets', + \ 'timeline', 'visibilityState', 'cookie', 'defaultView', + \ 'designMode', 'dir', 'domain', 'lastModified', 'location', + \ 'readyState', 'referrer', 'title', 'URL', 'activeElement', + \ 'fullscreenElement', 'styleSheets'] + let documeth = ['adoptNode', 'close', 'createAttribute', + \ 'createAttributeNS', 'createCDATASection', 'createComment', + \ 'createDocumentFragment', 'createElement', 'createElementNS', + \ 'createEvent', 'createExpression', 'createNSResolver', + \ 'createNodeIterator', 'createProcessingInstruction', 'createRange', + \ 'createTextNode', 'createTouchList', 'createTreeWalker', + \ 'enableStyleSheetsForSet', 'evaluate', 'focus', 'getElementById', + \ 'getElementById', 'getElementsByClassName', 'getElementsByName', + \ 'getElementsByTagName', 'getElementsByTagNameNS', + \ 'hasStorageAccess', 'importNode', 'onClick', 'onDblClick', + \ 'onFocus', 'onKeyDown', 'onKeyPress', 'onKeyUp', 'onMouseDown', + \ 'onMouseMove', 'onMouseOut', 'onMouseOver', 'onMouseUp', + \ 'onResize', 'open', 'querySelector', 'querySelectorAll', + \ 'requestStorageAccess', 'write', 'writeln'] + call map(documeth, 'v:val."("') let docuxprop = ['attributes', 'childNodes', 'doctype', 'documentElement', 'firstChild', \ 'implementation', 'namespaceURI', 'nextSibling', 'nodeName', 'nodeType', @@ -368,9 +388,11 @@ function! javascriptcomplete#CompleteJS(findstart, base) let xdomelemprop = ['attributes', 'childNodes', 'firstChild', 'lastChild', \ 'namespaceURI', 'nextSibling', 'nodeName', 'nodeType', 'nodeValue', \ 'ownerDocument', 'parentNode', 'prefix', 'previousSibling', 'tagName'] - let xdomelemmeth = ['appendChild', 'cloneNode', 'getAttribute', 'getAttributeNode', - \ 'getElementsByTagName', 'hasChildNodes', 'insertBefore', 'normalize', - \ 'removeAttribute', 'removeAttributeNode', 'removeChild', 'replaceChild', + let xdomelemmeth = ['appendChild', 'addEventListener', 'cloneNode', + \ 'dispatchEvent', 'getAttribute', 'getAttributeNode', + \ 'getElementsByTagName', 'hasChildNodes', 'insertBefore', + \ 'normalize', 'removeAttribute', 'removeAttributeNode', + \ 'removeChild', 'removeEventListener', 'replaceChild', \ 'setAttribute', 'setAttributeNode'] call map(xdomelemmeth, 'v:val."("') let xdomelems = xdomelemprop + xdomelemmeth diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index d97ac684c3..78a86315a3 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -18,7 +18,7 @@ function! man#init() abort endtry endfunction -function! man#open_page(count, count1, mods, ...) abort +function! man#open_page(count, mods, ...) abort if a:0 > 2 call s:error('too many arguments') return @@ -39,9 +39,7 @@ function! man#open_page(count, count1, mods, ...) abort endif try let [sect, name] = s:extract_sect_and_name_ref(ref) - if a:count ==# a:count1 - " v:count defaults to 0 which is a valid section, and v:count1 defaults to - " 1, also a valid section. If they are equal, count explicitly set. + if a:count >= 0 let sect = string(a:count) endif let path = s:verify_exists(sect, name) diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim index 3265fdde36..6afe64de84 100644 --- a/runtime/autoload/tutor.vim +++ b/runtime/autoload/tutor.vim @@ -120,6 +120,12 @@ function! s:Locale() let l:lang = v:lang elseif $LC_ALL =~ '\a\a' let l:lang = $LC_ALL + elseif $LC_MESSAGES =~ '\a\a' || $LC_MESSAGES ==# "C" + " LC_MESSAGES=C can be used to explicitly ask for English messages while + " keeping LANG non-English; don't set l:lang then. + if $LC_MESSAGES =~ '\a\a' + let l:lang = $LC_MESSAGES + endif elseif $LANG =~ '\a\a' let l:lang = $LANG else diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 58633455c3..485c93b0dd 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|): > @@ -646,6 +646,9 @@ nvim_create_namespace({name}) *nvim_create_namespace()* nvim_del_current_line() *nvim_del_current_line()* Deletes the current line. + Attributes: ~ + not allowed when |textlock| is active + nvim_del_keymap({mode}, {lhs}) *nvim_del_keymap()* Unmaps a global |mapping| for the given mode. @@ -755,6 +758,15 @@ nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()* feedkeys() vim_strsave_escape_csi +nvim_get_all_options_info() *nvim_get_all_options_info()* + Gets the option information for all options. + + The dictionary has the full option names as keys and option + metadata dictionaries as detailed at |nvim_get_option_info|. + + Return: ~ + dictionary of all options + nvim_get_api_info() *nvim_get_api_info()* Returns a 2-tuple (Array), where item 0 is the current channel id and item 1 is the |api-metadata| map (Dictionary). @@ -933,6 +945,29 @@ nvim_get_option({name}) *nvim_get_option()* Return: ~ Option value (global) +nvim_get_option_info({name}) *nvim_get_option_info()* + Gets the option information for one option + + Resulting dictionary has keys: + • name: Name of the option (like 'filetype') + • shortname: Shortened name of the option (like 'ft') + • type: type of option ("string", "integer" or "boolean") + • default: The default value for the option + • was_set: Whether the option was set. + • last_set_sid: Last set script id (if any) + • last_set_linenr: line number where option was set + • last_set_chan: Channel where option was set (0 for local) + • scope: one of "global", "win", or "buf" + • global_local: whether win or buf option has a global value + • commalist: List of comma separated values + • flaglist: List of single char flags + + Parameters: ~ + {name} Option name + + Return: ~ + Option Information + nvim_get_proc({pid}) *nvim_get_proc()* Gets info describing process `pid` . @@ -950,11 +985,16 @@ nvim_get_runtime_file({name}, {all}) *nvim_get_runtime_file()* 'name' can contain wildcards. For example nvim_get_runtime_file("colors/*.vim", true) will return all - color scheme files. + color scheme files. Always use forward slashes (/) in the + search pattern for subdirectories regardless of platform. It is not an error to not find any files. An empty array is returned then. + To find a directory, `name` must end with a forward slash, + like "rplugin/python/". Without the slash it would instead + look for an ordinary file called "rplugin/python". + Attributes: ~ {fast} @@ -1136,6 +1176,9 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* {relative='win', width=12, height=3, bufpos={100,10}}) < + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {buffer} Buffer to display, or 0 for current buffer {enter} Enter the window (make it the current window) @@ -1314,6 +1357,9 @@ nvim_paste({data}, {crlf}, {phase}) *nvim_paste()* calls are ignored ("drained") until the next paste is initiated (phase 1 or -1). + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {data} Multiline input. May be binary (containing NUL bytes). @@ -1334,6 +1380,9 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()* Compare |:put| and |p| which are always linewise. + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {lines} |readfile()|-style list of lines. |channel-lines| @@ -1451,6 +1500,9 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes}) nvim_set_current_buf({buffer}) *nvim_set_current_buf()* Sets the current buffer. + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {buffer} Buffer handle @@ -1463,18 +1515,27 @@ nvim_set_current_dir({dir}) *nvim_set_current_dir()* nvim_set_current_line({line}) *nvim_set_current_line()* Sets the current line. + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {line} Line contents nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()* Sets the current tabpage. + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {tabpage} Tabpage handle nvim_set_current_win({window}) *nvim_set_current_win()* Sets the current window. + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {window} Window handle @@ -1535,7 +1596,9 @@ nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()* {ns_id} number of namespace for this highlight {name} highlight group name, like ErrorMsg {val} highlight definiton map, like - |nvim_get_hl_by_name|. + |nvim_get_hl_by_name|. in addition the following + keys are also recognized: `default` : don't + override existing definition, like `hi default` nvim_set_hl_ns({ns_id}) *nvim_set_hl_ns()* Set active namespace for highlights. @@ -1741,6 +1804,8 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* • utf_sizes: include UTF-32 and UTF-16 size of the replaced region, as args to `on_lines` . + • preview: also attach to command preview + (i.e. 'inccommand') events. Return: ~ False if attach failed (invalid parameter, or buffer isn't @@ -1819,6 +1884,9 @@ nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()* nvim_buf_delete({buffer}, {opts}) *nvim_buf_delete()* Deletes the buffer. See |:bwipeout| + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {buffer} Buffer handle, or 0 for current buffer {opts} Optional parameters. Keys: @@ -2115,6 +2183,9 @@ nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, {replacement}) Out-of-bounds indices are clamped to the nearest valid value, unless `strict_indexing` is set. + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {buffer} Buffer handle, or 0 for current buffer {start} First line index @@ -2139,6 +2210,32 @@ nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()* {name} Option name {value} Option value + *nvim_buf_set_text()* +nvim_buf_set_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col}, + {replacement}) + Sets (replaces) a range in the buffer + + This is recommended over nvim_buf_set_lines when only + modifying parts of a line, as extmarks will be preserved on + non-modified parts of the touched lines. + + Indexing is zero-based and end-exclusive. + + To insert text at a given index, set `start` and `end` ranges + to the same index. To delete a range, set `replacement` to an + array containing an empty string, or simply an empty array. + + Prefer nvim_buf_set_lines when adding or deleting entire lines + only. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {start_row} First line index + {start_column} Last column + {end_row} Last line index + {end_column} Last column + {replacement} Array of lines to use as replacement + nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* Sets a buffer-scoped (b:) variable @@ -2192,6 +2289,9 @@ Window Functions *api-window* nvim_win_close({window}, {force}) *nvim_win_close()* Closes the window (like |:close| with a |window-ID|). + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {window} Window handle, or 0 for current window {force} Behave like `:close!` The last window of a @@ -2317,6 +2417,9 @@ nvim_win_is_valid({window}) *nvim_win_is_valid()* nvim_win_set_buf({window}, {buffer}) *nvim_win_set_buf()* Sets the current buffer in a window, without side-effects + Attributes: ~ + not allowed when |textlock| is active + Parameters: ~ {window} Window handle, or 0 for current window {buffer} Buffer handle 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/eval.txt b/runtime/doc/eval.txt index 343e35bf66..384bdd63a4 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1600,7 +1600,7 @@ v:event Dictionary of event data for the current |autocommand|. Valid regtype Type of register as returned by |getregtype()|. visual Selection is visual (as opposed to, - e.g., via motion). + e.g., via motion). completed_item Current selected complete item on |CompleteChanged|, Is `{}` when no complete item selected. @@ -1783,7 +1783,7 @@ v:msgpack_types Dictionary containing msgpack types used by |msgpackparse()| of msgpack types, use |is| operator. *v:null* *null-variable* -v:null Special value used to put "null" in JSON and NIL in msgpack. +v:null Special value used to put "null" in JSON and NIL in msgpack. See |json_encode()|. This value is converted to "v:null" when used as a String (e.g. in |expr5| with string concatenation operator) and to zero when used as a Number (e.g. in |expr5| @@ -2184,6 +2184,7 @@ getjumplist([{winnr} [, {tabnr}]]) getline({lnum}) String line {lnum} of current buffer getline({lnum}, {end}) List lines {lnum} to {end} of current buffer getloclist({nr} [, {what}]) List list of location list items +getmarklist([{expr}]) List list of global/local marks getmatches([{win}]) List list of current matches getpid() Number process ID of Vim getpos({expr}) List position of cursor, mark, etc. @@ -4149,7 +4150,7 @@ function({name} [, {arglist}] [, {dict}]) garbagecollect([{atexit}]) *garbagecollect()* Cleanup unused |Lists| and |Dictionaries| that have circular references. - + There is hardly ever a need to invoke this function, as it is automatically done when Vim runs out of memory or is waiting for the user to press a key after 'updatetime'. Items without @@ -4649,6 +4650,24 @@ getloclist({nr},[, {what}]) *getloclist()* field is applicable only when called from a location list window. See |location-list-file-window| for more details. +getmarklist([{expr}] *getmarklist()* + Without the {expr} argument returns a |List| with information + about all the global marks. |mark| + + If the optional {expr} argument is specified, returns the + local marks defined in buffer {expr}. For the use of {expr}, + see |bufname()|. + + Each item in the retuned List is a |Dict| with the following: + name - name of the mark prefixed by "'" + pos - a |List| with the position of the mark: + [bufnum, lnum, col, off] + Refer to |getpos()| for more information. + file - file name + + Refer to |getpos()| for getting information about a specific + mark. + getmatches([{win}]) *getmatches()* Returns a |List| with all matches previously defined for the current window by |matchadd()| and the |:match| commands. @@ -4915,7 +4934,7 @@ getwininfo([{winid}]) *getwininfo()* quickfix 1 if quickfix or location list window terminal 1 if a terminal window tabnr tab page number - topline first displayed buffer line + topline first displayed buffer line variables a reference to the dictionary with window-local variables width window width @@ -5055,7 +5074,7 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The Vim's compile-time feature-names (prefixed with "+") are not recognized because Nvim is always compiled with all possible - features. |feature-compile| + features. |feature-compile| Feature names can be: 1. Nvim version. For example the "nvim-0.2.1" feature means @@ -5080,7 +5099,7 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The ttyin input is a terminal (tty) ttyout output is a terminal (tty) unix Unix system. - *vim_starting* True during |startup|. + *vim_starting* True during |startup|. win32 Windows system (32 or 64 bit). win64 Windows system (64 bit). wsl WSL (Windows Subsystem for Linux) system @@ -5665,7 +5684,7 @@ json_encode({expr}) *json_encode()* |msgpack-special-dict|), values with self-referencing containers, strings which contain non-UTF-8 characters, pseudo-UTF-8 strings which contain codepoints reserved for - surrogate pairs (such strings are not valid UTF-8 strings). + surrogate pairs (such strings are not valid UTF-8 strings). Non-printable characters are converted into "\u1234" escapes or special escapes like "\t", other are dumped as-is. @@ -5828,7 +5847,7 @@ map({expr1}, {expr2}) *map()* {expr1} must be a |List| or a |Dictionary|. Replace each item in {expr1} with the result of evaluating {expr2}. {expr2} must be a |string| or |Funcref|. - + If {expr2} is a |string|, inside {expr2} |v:val| has the value of the current item. For a |Dictionary| |v:key| has the key of the current item and for a |List| |v:key| has the index of @@ -6095,8 +6114,8 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]]) - A list with three numbers, e.g., [23, 11, 3]. As above, but the third number gives the length of the highlight in bytes. - Entries with zero and negative line numbers are silently - ignored, as well as entries with negative column numbers and + Entries with zero and negative line numbers are silently + ignored, as well as entries with negative column numbers and lengths. The maximum number of positions is 8. @@ -7589,7 +7608,7 @@ setpos({expr}, {list}) setqflist({list} [, {action}[, {what}]]) *setqflist()* Create or replace or add to the quickfix list. - + When {what} is not present, use the items in {list}. Each item must be a dictionary. Non-dictionary items in {list} are ignored. Each dictionary item can contain the following @@ -7630,12 +7649,12 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()* 'a' The items from {list} are added to the existing quickfix list. If there is no existing list, then a new list is created. - + 'r' The items from the current quickfix list are replaced with the items from {list}. This can also be used to clear the list: > :call setqflist([], 'r') -< +< 'f' All the quickfix lists in the quickfix stack are freed. @@ -8004,7 +8023,7 @@ sign_place({id}, {group}, {name}, {expr} [, {dict}]) empty string. {group} functions as a namespace for {id}, thus two groups can use the same IDs. Refer to |sign-identifier| for more information. - + {name} refers to a defined sign. {expr} refers to a buffer name or number. For the accepted values, see |bufname()|. @@ -8441,8 +8460,6 @@ strftime({format} [, {time}]) *strftime()* :echo strftime("%H:%M") 11:55 :echo strftime("%c", getftime("file.c")) Show mod time of file.c. -< Not available on all systems. To check use: > - :if exists("*strftime") strgetchar({str}, {index}) *strgetchar()* Get character {index} from {str}. This uses a character @@ -8786,7 +8803,7 @@ system({cmd} [, {input}]) *system()* *E677* Note: Use |shellescape()| or |::S| with |expand()| or |fnamemodify()| to escape special characters in a command - argument. Newlines in {cmd} may cause the command to fail. + argument. Newlines in {cmd} may cause the command to fail. The characters in 'shellquote' and 'shellxquote' may also cause trouble. @@ -9333,6 +9350,13 @@ wincol() The result is a Number, which is the virtual column of the cursor in the window. This is counting screen cells from the left side of the window. The leftmost column is one. + *windowsversion()* +windowsversion() + The result is a String. For MS-Windows it indicates the OS + version. E.g, Windows 10 is "10.0", Windows 8 is "6.2", + Windows XP is "5.1". For non-MS-Windows systems the result is + an empty string. + winheight({nr}) *winheight()* The result is a Number, which is the height of window {nr}. {nr} can be the window number or the |window-ID|. diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index afcacad460..17de1d8533 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1173,9 +1173,11 @@ tag command action ~ |:caddbuffer| :cad[dbuffer] add errors from buffer |:caddexpr| :cadde[xpr] add errors from expr |:caddfile| :caddf[ile] add error message to current quickfix list +|:cafter| :caf[ter] go to error after current cursor |:call| :cal[l] call a function |:catch| :cat[ch] part of a :try command -|:cbelow| :cbe[low] go to error below current line +|:cbefore| :cbef[ore] go to error before current cursor +|:cbelow| :cbel[ow] go to error below current line |:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window |:cbuffer| :cb[uffer] parse error messages and jump to first error |:cc| :cc go to specific error @@ -1336,10 +1338,12 @@ tag command action ~ |:laddexpr| :lad[dexpr] add locations from expr |:laddbuffer| :laddb[uffer] add locations from buffer |:laddfile| :laddf[ile] add locations to current location list +|:lafter| :laf[ter] go to location after current cursor |:last| :la[st] go to the last file in the argument list |:language| :lan[guage] set the language (locale) |:later| :lat[er] go to newer change, redo -|:lbelow| :lbe[low] go to location below current line +|:lbefore| :lbef[ore] go to location before current cursor +|:lbelow| :lbel[ow] go to location below current line |:lbottom| :lbo[ttom] scroll to the bottom of the location window |:lbuffer| :lb[uffer] parse locations and jump to first location |:lcd| :lc[d] change directory locally diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index c8a44dfb75..6ca7b52fff 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|. @@ -176,6 +176,7 @@ specification. These LSP requests/notifications are defined by default: textDocument/typeDefinition* window/logMessage window/showMessage + window/showMessageRequest workspace/applyEdit workspace/symbol @@ -247,7 +248,7 @@ For |lsp-notification|, each |lsp-handler| has this signature: > of a particular handler. For an example, see: - |vim.lsp.diagnostics.on_publish_diagnostics()| + |vim.lsp.diagnostic.on_publish_diagnostics()| To configure a particular |lsp-handler|, see: |lsp-handler-configuration| @@ -257,7 +258,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()|, @@ -715,6 +716,15 @@ start_client({config}) *vim.lsp.start_client()* The following parameters describe fields in the {config} table. +> + + -- In attach function for the client, you can do: + local custom_attach = function(client) + if client.config.flags then + client.config.flags.allow_incremental_sync = true + end + end +< Parameters: ~ {root_dir} (required, string) Directory where the @@ -746,6 +756,10 @@ start_client({config}) *vim.lsp.start_client()* array. {handlers} Map of language server method names to |lsp-handler| + {settings} Map with language server specific + settings. These are returned to the language server if + requested via `workspace/configuration`. Keys are + case-sensitive. {init_options} Values to pass in the initialization request as `initializationOptions` . See `initialize` in the LSP spec. @@ -796,6 +810,11 @@ start_client({config}) *vim.lsp.start_client()* in the initialize request. Invalid/empty values will default to "off" + {flags} A table with flags for the client. The + current (experimental) flags are: + • allow_incremental_sync (bool, default + false): Allow using on_line callbacks + for lsp Return: ~ Client id. |vim.lsp.get_client_by_id()| Note: client may @@ -833,6 +852,12 @@ with({handler}, {override_config}) *vim.lsp.with()* ============================================================================== Lua module: vim.lsp.buf *lsp-buf* + *vim.lsp.buf.add_workspace_folder()* +add_workspace_folder({workspace_folder}) + Add the folder at path to the workspace folders. If {path} is + not provided, the user will be prompted for a path using + |input()|. + clear_references() *vim.lsp.buf.clear_references()* Removes document highlights from current buffer. @@ -935,6 +960,9 @@ incoming_calls() *vim.lsp.buf.incoming_calls()* |quickfix| window. If the symbol can resolve to multiple items, the user can pick one in the |inputlist|. +list_workspace_folders() *vim.lsp.buf.list_workspace_folders()* + List workspace folders. + outgoing_calls() *vim.lsp.buf.outgoing_calls()* Lists all the items that are called by the symbol under the cursor in the |quickfix| window. If the symbol can resolve to @@ -961,6 +989,9 @@ range_formatting({options}, {start_pos}, {end_pos}) Parameters: ~ {options} Table with valid `FormattingOptions` entries. {start_pos} ({number, number}, optional) mark-indexed + position. Defaults to the start of the last + visual selection. + {end_pos} ({number, number}, optional) mark-indexed position. Defaults to the end of the last visual selection. @@ -974,6 +1005,12 @@ references({context}) *vim.lsp.buf.references()* See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references + *vim.lsp.buf.remove_workspace_folder()* +remove_workspace_folder({workspace_folder}) + Remove the folder at path from the workspace folders. If + {path} is not provided, the user will be prompted for a path + using |input()|. + rename({new_name}) *vim.lsp.buf.rename()* Renames all references to the symbol under the cursor. @@ -996,19 +1033,6 @@ type_definition() *vim.lsp.buf.type_definition()* Jumps to the definition of the type of the symbol under the cursor. -add_workspace_folder({path}) *vim.lsp.buf.add_workspace_folder()* - Add the folder at path to the workspace folders. If {path} is - not provided, the user will be prompted for a path using - |input()|. - -remove_workspace_folder({path}) *vim.lsp.buf.remove_workspace_folder()* - Remove the folder at path from the workspace folders. If - {path} is not provided, the user will be prompted for - a path using |input()|. - -list_workspace_folders() *vim.lsp.buf.list_workspace_folders()* - List all folders in the workspace. - workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()* Lists all symbols in the current workspace in the quickfix window. @@ -1062,9 +1086,9 @@ get_count({bufnr}, {severity}, {client_id}) let sl = '' if luaeval('not vim.tbl_isempty(vim.lsp.buf_get_clients(0))') let sl.='%#MyStatuslineLSP#E:' - let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.diagnostic.get_count(vim.fn.bufnr('%'), [[Error]])")}' + let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.diagnostic.get_count(0, [[Error]])")}' let sl.='%#MyStatuslineLSP# W:' - let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.diagnostic.get_count(vim.fn.bufnr('%'), [[Warning]])")}' + let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.diagnostic.get_count(0, [[Warning]])")}' else let sl.='%#MyStatuslineLSPErrors#off' endif @@ -1101,10 +1125,10 @@ get_line_diagnostics({bufnr}, {line_nr}, {opts}, {client_id}) diagnostics. get_next({opts}) *vim.lsp.diagnostic.get_next()* - Get the previous diagnostic closest to the cursor_position + Get the next diagnostic closest to the cursor_position Parameters: ~ - {opts} table See |vim.lsp.diagnostics.goto_next()| + {opts} table See |vim.lsp.diagnostic.goto_next()| Return: ~ table Next diagnostic @@ -1114,7 +1138,7 @@ get_next_pos({opts}) *vim.lsp.diagnostic.get_next_pos()* current buffer. Parameters: ~ - {opts} table See |vim.lsp.diagnostics.goto_next()| + {opts} table See |vim.lsp.diagnostic.goto_next()| Return: ~ table Next diagnostic position @@ -1123,7 +1147,7 @@ get_prev({opts}) *vim.lsp.diagnostic.get_prev()* Get the previous diagnostic closest to the cursor_position Parameters: ~ - {opts} table See |vim.lsp.diagnostics.goto_next()| + {opts} table See |vim.lsp.diagnostic.goto_next()| Return: ~ table Previous diagnostic @@ -1133,7 +1157,7 @@ get_prev_pos({opts}) *vim.lsp.diagnostic.get_prev_pos()* current buffer. Parameters: ~ - {opts} table See |vim.lsp.diagnostics.goto_next()| + {opts} table See |vim.lsp.diagnostic.goto_next()| Return: ~ table Previous diagnostic position @@ -1196,7 +1220,7 @@ goto_prev({opts}) *vim.lsp.diagnostic.goto_prev()* Move to the previous diagnostic Parameters: ~ - {opts} table See |vim.lsp.diagnostics.goto_next()| + {opts} table See |vim.lsp.diagnostic.goto_next()| *vim.lsp.diagnostic.on_publish_diagnostics()* on_publish_diagnostics({_}, {_}, {params}, {client_id}, {_}, {config}) @@ -1654,6 +1678,14 @@ make_text_document_params() *vim.lsp.util.make_text_document_params()* See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier + *vim.lsp.util.make_workspace_params()* +make_workspace_params({added}, {removed}) + Create the workspace params + + Parameters: ~ + {added} + {removed} + *vim.lsp.util.open_floating_preview()* open_floating_preview({contents}, {filetype}, {opts}) Shows contents in a floating window. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 6c42dd6739..2a757bbed9 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -734,6 +734,8 @@ A jump table for the options with a short description can be found at |Q_op|. eol allow backspacing over line breaks (join lines) start allow backspacing over the start of insert; CTRL-W and CTRL-U stop once at the start of insert. + nostop like start, except CTRL-W and CTRL-U do not stop at the start of + insert. When the value is empty, Vi compatible backspacing is used. @@ -742,6 +744,7 @@ A jump table for the options with a short description can be found at |Q_op|. 0 same as ":set backspace=" (Vi compatible) 1 same as ":set backspace=indent,eol" 2 same as ":set backspace=indent,eol,start" + 3 same as ":set backspace=indent,eol,nostop" *'backup'* *'bk'* *'nobackup'* *'nobk'* 'backup' 'bk' boolean (default off) @@ -2438,7 +2441,7 @@ A jump table for the options with a short description can be found at |Q_op|. 'foldcolumn' 'fdc' string (default "0") local to window When and how to draw the foldcolumn. Valid values are: - "auto": resize to the maximum amount of folds to display. + "auto": resize to the minimum amount of folds to display. "auto:[1-9]": resize to accommodate multiple folds up to the selected level 0: to disable foldcolumn @@ -2609,7 +2612,7 @@ A jump table for the options with a short description can be found at |Q_op|. when internal formatting is used. Make sure the cursor is kept in the same spot relative to the text then! The |mode()| function will return "i" or "R" in this situation. - + When the expression evaluates to non-zero Vim will fall back to using the internal format mechanism. @@ -3184,7 +3187,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'inccommand'* *'icm'* 'inccommand' 'icm' string (default "") global - + "nosplit": Shows the effects of a command incrementally, as you type. "split" : Also shows partial off-screen results in a preview window. @@ -6838,7 +6841,7 @@ A jump table for the options with a short description can be found at |Q_op|. a built-in |highlight-groups| item to be overridden by {hl} group in the window. Only built-in |highlight-groups| are supported, not syntax highlighting (use |:ownsyntax| for that). - + Highlights of vertical separators are determined by the window to the left of the separator. The 'tabline' highlight of a tabpage is decided by the last-focused window of the tabpage. Highlights of diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index d6ff3ea9ea..fab3b11430 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -74,7 +74,7 @@ processing a quickfix or location list command, it will be aborted. *:cc* :cc[!] [nr] Display error [nr]. If [nr] is omitted, the same - error is displayed again. Without [!] this doesn't +:[nr]cc[!] error is displayed again. Without [!] this doesn't work when jumping to another buffer, the current buffer has been changed, there is the only window for the buffer and both 'hidden' and 'autowrite' are off. @@ -83,10 +83,13 @@ processing a quickfix or location list command, it will be aborted. there is another window for this buffer. The 'switchbuf' settings are respected when jumping to a buffer. + When used in the quickfix window the line number can + be used, including "." for the current line and "$" + for the last line. *:ll* :ll[!] [nr] Same as ":cc", except the location list for the - current window is used instead of the quickfix list. +:[nr]ll[!] current window is used instead of the quickfix list. *:cn* *:cne* *:cnext* *E553* :[count]cn[ext][!] Display the [count] next error in the list that @@ -125,8 +128,8 @@ processing a quickfix or location list command, it will be aborted. :[count]lab[ove] Same as ":cabove", except the location list for the current window is used instead of the quickfix list. - *:cbe* *:cbelow* -:[count]cbe[low] Go to the [count] error below the current line in the + *:cbel* *:cbelow* +:[count]cbel[ow] Go to the [count] error below the current line in the current buffer. If [count] is omitted, then 1 is used. If there are no errors, then an error message is displayed. Assumes that the entries in a quickfix @@ -136,8 +139,36 @@ processing a quickfix or location list command, it will be aborted. exceeds the number of entries below the current line, then the last error in the file is selected. - *:lbe* *:lbelow* -:[count]lbe[low] Same as ":cbelow", except the location list for the + *:lbel* *:lbelow* +:[count]lbel[ow] Same as ":cbelow", except the location list for the + current window is used instead of the quickfix list. + + *:cbe* *:cbefore* +:[count]cbe[fore] Go to the [count] error before the current cursor + position in the current buffer. If [count] is + omitted, then 1 is used. If there are no errors, then + an error message is displayed. Assumes that the + entries in a quickfix list are sorted by their buffer, + line and column numbers. If [count] exceeds the + number of entries before the current position, then + the first error in the file is selected. + + *:lbe* *:lbefore* +:[count]lbe[fore] Same as ":cbefore", except the location list for the + current window is used instead of the quickfix list. + + *:caf* *:cafter* +:[count]caf[ter] Go to the [count] error after the current cursor + position in the current buffer. If [count] is + omitted, then 1 is used. If there are no errors, then + an error message is displayed. Assumes that the + entries in a quickfix list are sorted by their buffer, + line and column numbers. If [count] exceeds the + number of entries after the current position, then + the last error in the file is selected. + + *:laf* *:lafter* +:[count]laf[ter] Same as ":cafter", except the location list for the current window is used instead of the quickfix list. *:cnf* *:cnfile* @@ -805,14 +836,19 @@ lists. They set one of the existing error lists as the current one. the current window instead of the quickfix list. *:chistory* *:chi* -:chi[story] Show the list of error lists. The current list is +:[count]chi[story] Show the list of error lists. The current list is marked with ">". The output looks like: error list 1 of 3; 43 errors ~ > error list 2 of 3; 0 errors ~ error list 3 of 3; 15 errors ~ + When [count] is given, then the count'th quickfix + list is made the current list. Example: > + " Make the 4th quickfix list current + :4chistory +< *:lhistory* *:lhi* -:lhi[story] Show the list of location lists, otherwise like +:[count]lhi[story] Show the list of location lists, otherwise like `:chistory`. When adding a new error list, it becomes the current list. @@ -1294,7 +1330,11 @@ Basic items %v virtual column number (finds a number representing screen column of the error (1 <tab> == 8 screen columns)) - %t error type (finds a single character) + %t error type (finds a single character): + e - error message + w - warning message + i - info message + n - note message %n error number (finds a number) %m error message (finds a string) %r matches the "rest" of a single-line file message %O/P/Q @@ -1365,6 +1405,7 @@ prefixes are: %E start of a multi-line error message %W start of a multi-line warning message %I start of a multi-line informational message + %N start of a multi-line note message %A start of a multi-line message (unspecified type) %> for next line start with current pattern again |efm-%>| %C continuation of a multi-line message diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index d4b6324bc3..604c969c64 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -180,7 +180,7 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. Read Ex commands from {file} in each directory given by 'runtimepath' and/or 'packpath'. There is no error for non-existing files. - + Example: > :runtime syntax/c.vim @@ -254,7 +254,7 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. *:packl* *:packloadall* :packl[oadall][!] Load all packages in the "start" directory under each entry in 'packpath'. - + First all the directories found are added to 'runtimepath', then the plugins found in the directories are sourced. This allows for a plugin to diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index b011db3dd3..23db809543 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -544,7 +544,7 @@ only supported by new versions of ctags (such as Exuberant ctags). the bar) and ;" is used to have Vi ignore the rest of the line. Example: APP file.c call cursor(3, 4)|;" v - + {field} .. A list of optional fields. Each field has the form: <Tab>{fieldname}:{value} diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index ae77b0a35a..911e7b8b47 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -197,11 +197,11 @@ query:iter_captures({node}, {bufnr}, {start_row}, {end_row}) as the node, i e to get syntax highlight matches in the current viewport) - The iterator returns two values, a numeric id identifying the capture - and the captured node. The following example shows how to get captures - by name: + The iterator returns three values, a numeric id identifying the capture, + the captured node, and metadata from any directives processing the match. + The following example shows how to get captures by name: > - for id, node in query:iter_captures(tree:root(), bufnr, first, last) do + for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do local name = query.captures[id] -- name of the capture in the query -- typically useful info about the node: local type = node:type() -- type of the captured node @@ -213,16 +213,19 @@ query:iter_matches({node}, {bufnr}, {start_row}, {end_row}) *query:iter_matches()* Iterate over all matches within a node. The arguments are the same as for |query:iter_captures()| but the iterated values are different: - an (1-based) index of the pattern in the query, and a table mapping - capture indices to nodes. If the query has more than one pattern - the capture table might be sparse, and e.g. `pairs` should be used and not - `ipairs`. Here an example iterating over all captures in - every match: + an (1-based) index of the pattern in the query, a table mapping + capture indices to nodes, and metadata from any directives processing the match. + If the query has more than one pattern the capture table might be sparse, + and e.g. `pairs()` method should be used over `ipairs`. + Here an example iterating over all captures in every match: > - for pattern, match in cquery:iter_matches(tree:root(), bufnr, first, last) do - for id,node in pairs(match) do + for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do + for id, node in pairs(match) do local name = query.captures[id] -- `node` was captured by the `name` capture in the match + + local node_data = metadata[id] -- Node level metadata + ... use the info here ... end end @@ -265,6 +268,29 @@ Here is a list of built-in predicates : Each predicate has a `not-` prefixed predicate that is just the negation of the predicate. +Treesitter Query Directive *lua-treesitter-directives* + +Treesitter queries can also contain `directives`. Directives store metadata for a node +or match and perform side effects. for example, the |set!| predicate sets metadata on +the match or node : > + ((identifier) @foo (#set! "type" "parameter")) + +Here is a list of built-in directives: + + `set!` *ts-directive-set!* + Sets key/value metadata for a specific node or match : > + ((identifier) @foo (#set! @foo "kind" "parameter")) + ((node1) @left (node2) @right (#set! "type" "pair")) +< + `offset!` *ts-predicate-offset!* + Takes the range of the captured node and applies the offsets + to it's range : > + ((idenfitier) @constant (#offset! @constant 0 1 0 -1)) +< This will generate a range object for the captured node with the + offsets applied. The arguments are + `({capture_id}, {start_row}, {start_col}, {end_row}, {end_col}, {key?})` + The default key is "offset". + *vim.treesitter.query.add_predicate()* vim.treesitter.query.add_predicate({name}, {handler}) @@ -277,10 +303,20 @@ vim.treesitter.query.list_predicates() This lists the currently available predicates to use in queries. + *vim.treesitter.query.add_directive()* +vim.treesitter.query.add_directive({name}, {handler}) + +This adds a directive with the name {name} to be used in queries. +{handler} should be a function whose signature will be : > + handler(match, pattern, bufnr, predicate, metadata) +Handlers can set match level data by setting directly on the metadata object `metadata.key = value` +Handlers can set node level data by using the capture id on the metadata table +`metadata[capture_id].key = value` + Treesitter syntax highlighting (WIP) *lua-treesitter-highlight* NOTE: This is a partially implemented feature, and not usable as a default -solution yet. What is documented here is a temporary interface indented +solution yet. What is documented here is a temporary interface intended for those who want to experiment with this feature and contribute to its development. diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index e5c6b9b1b7..82406898c8 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -170,7 +170,7 @@ the editor. `mouse_shape`: (To be implemented.) Some keys are missing in some modes. - + The following keys are deprecated: `hl_id`: Use `attr_id` instead. @@ -213,11 +213,9 @@ the editor. ["mouse_on"] ["mouse_off"] - |'mouse'| was enabled/disabled in the current editor mode. Useful for - a terminal UI, or other situations where Nvim mouse would conflict - with other usages of the mouse. UIs may ignore this and always send - mouse input, because 'mouse' decides the behavior of |nvim_input()| - implicitly. + 'mouse' was enabled/disabled in the current editor mode. Useful for + a terminal UI, or embedding into an application where Nvim mouse would + conflict with other usages of the mouse. Other UI:s may ignore this event. ["busy_start"] ["busy_stop"] @@ -462,7 +460,7 @@ is not active. New UIs should implement |ui-linegrid| instead. ["set_scroll_region", top, bot, left, right] Define the scroll region used by `scroll` below. - + Note: ranges are end-inclusive, which is inconsistent with API conventions. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 31da51bfbe..e92e464c6a 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -723,6 +723,7 @@ Cursor and mark position: *cursor-functions* *mark-functions* getcurpos() get position of the cursor getpos() get position of cursor, mark, etc. setpos() set position of cursor, mark, etc. + getmarklist() list of global/local marks byte2line() get line number at a specific byte count line2byte() byte count at a specific line diff_filler() get the number of filler lines above a line diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 32551815b0..b6885d6e25 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -46,26 +46,26 @@ CTRL-L Clears and redraws the screen. The redraw may happen ga Print the ascii value of the character under the cursor in decimal, hexadecimal and octal. Mnemonic: Get Ascii value. - + For example, when the cursor is on a 'R': <R> 82, Hex 52, Octal 122 ~ When the character is a non-standard ASCII character, but printable according to the 'isprint' option, the non-printable version is also given. - + When the character is larger than 127, the <M-x> form is also printed. For example: <~A> <M-^A> 129, Hex 81, Octal 201 ~ <p> <|~> <M-~> 254, Hex fe, Octal 376 ~ (where <p> is a special character) - + The <Nul> character in a file is stored internally as <NL>, but it will be shown as: <^@> 0, Hex 00, Octal 000 ~ - + If the character has composing characters these are also shown. The value of 'maxcombine' doesn't matter. - + If the character can be inserted as a digraph, also output the two characters that can be used to create the character: @@ -317,11 +317,11 @@ g8 Print the hex values of the bytes used in the optional. :redi[r] @{a-z}>> Append messages to register {a-z}. -:redi[r] @*> +:redi[r] @*> :redi[r] @+> Redirect messages to the selection or clipboard. For backward compatibility, the ">" after the register name can be omitted. See |quotestar| and |quoteplus|. -:redi[r] @*>> +:redi[r] @*>> :redi[r] @+>> Append messages to the selection or clipboard. :redi[r] @"> Redirect messages to the unnamed register. For diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index b5623f4ea4..2c3ffcbe9a 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -124,7 +124,7 @@ CTRL-W CTRL-S *CTRL-W_CTRL-S* :[N]sp[lit] [++opt] [+cmd] [file] *:sp* *:split* Split current window in two. The result is two viewports on the same file. - + Make the new window N high (default is to use half the height of the current window). Reduces the current window height to create room (and others, if the 'equalalways' option is set, diff --git a/runtime/filetype.vim b/runtime/filetype.vim index b9d2a43d5d..959fe35662 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -495,6 +495,9 @@ au BufNewFile,BufRead *.com call dist#ft#BindzoneCheck('dcl') " DOT au BufNewFile,BufRead *.dot,*.gv setf dot +" Dune +au BufNewFile,BufRead jbuild,dune,dune-project,dune-workspace setf dune + " Dylan - lid files au BufNewFile,BufRead *.lid setf dylanlid @@ -623,6 +626,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 @@ -1118,8 +1124,8 @@ au BufNewFile,BufRead *.nse setf lua " NSIS au BufNewFile,BufRead *.nsi,*.nsh setf nsis -" OCAML -au BufNewFile,BufRead *.ml,*.mli,*.mll,*.mly,.ocamlinit setf ocaml +" OCaml +au BufNewFile,BufRead *.ml,*.mli,*.mll,*.mly,.ocamlinit,*.mlt,*.mlp,*.mlip,*.mli.cppo,*.ml.cppo setf ocaml " Occam au BufNewFile,BufRead *.occ setf occam @@ -1127,6 +1133,9 @@ au BufNewFile,BufRead *.occ setf occam " Omnimark au BufNewFile,BufRead *.xom,*.xin setf omnimark +" OPAM +au BufNewFile,BufRead opam,*.opam,*.opam.template setf opam + " OpenROAD au BufNewFile,BufRead *.or setf openroad @@ -1546,6 +1555,9 @@ au BufNewFile,BufRead *.scm,*.ss,*.rkt setf scheme " Screen RC au BufNewFile,BufRead .screenrc,screenrc setf screen +" Sexplib +au BufNewFile,BufRead *.sexp setf sexplib + " Simula au BufNewFile,BufRead *.sim setf simula @@ -1744,6 +1756,9 @@ au BufNewFile,BufRead *.tf,.tfrc,tfrc setf tf " tmux configuration au BufNewFile,BufRead {.,}tmux*.conf setf tmux +" TOML +au BufNewFile,BufRead *.toml setf toml + " TPP - Text Presentation Program au BufNewFile,BufReadPost *.tpp setf tpp @@ -1768,8 +1783,13 @@ au BufNewFile,BufReadPost *.tutor setf tutor " TWIG files au BufNewFile,BufReadPost *.twig setf twig -" Typescript -au BufNewFile,BufReadPost *.ts setf typescript +" Typescript or Qt translation file (which is XML) +au BufNewFile,BufReadPost *.ts + \ if getline(1) =~ '<?xml' | + \ setf xml | + \ else | + \ setf typescript | + \ endif " TypeScript with React au BufNewFile,BufRead *.tsx setf typescriptreact diff --git a/runtime/ftplugin/elm.vim b/runtime/ftplugin/elm.vim new file mode 100644 index 0000000000..1e10346186 --- /dev/null +++ b/runtime/ftplugin/elm.vim @@ -0,0 +1,18 @@ +" Elm filetype plugin file +" Language: Elm +" Maintainer: Andreas Scharf <as@99n.de> +" Latest Revision: 2020-05-29 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:cpo_save = &cpo +set cpo&vim + +setlocal comments=s1fl:{-,mb:\ ,ex:-},:-- +setlocal commentstring=--\ %s + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/ftplugin/javascript.vim b/runtime/ftplugin/javascript.vim index af5e4a920c..9a0b29e0ca 100644 --- a/runtime/ftplugin/javascript.vim +++ b/runtime/ftplugin/javascript.vim @@ -1,11 +1,11 @@ " Vim filetype plugin file -" Language: Javascript -" Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2008 Jun 15 -" URL: http://gus.gscit.monash.edu.au/~djkea2/vim/ftplugin/javascript.vim +" Language: Javascript +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2020 Jun 23 +" Contributor: Romain Lafourcade <romainlafourcade@gmail.com> if exists("b:did_ftplugin") - finish + finish endif let b:did_ftplugin = 1 @@ -13,7 +13,7 @@ let s:cpo_save = &cpo set cpo-=C " Set 'formatoptions' to break comment lines but not other lines, -" " and insert the comment leader when hitting <CR> or using "o". +" and insert the comment leader when hitting <CR> or using "o". setlocal formatoptions-=t formatoptions+=croql " Set completion with CTRL-X CTRL-O to autoloaded function. @@ -26,13 +26,50 @@ setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// setlocal commentstring=//%s -" Change the :browse e filter to primarily show Java-related files. -if has("gui_win32") - let b:browsefilter="Javascript Files (*.js)\t*.js\n" . - \ "All Files (*.*)\t*.*\n" +" Change the :browse e filter to primarily show JavaScript-related files. +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = + \ "JavaScript Files (*.js)\t*.js\n" + \ .. "JSX Files (*.jsx)\t*.jsx\n" + \ .. "JavaScript Modules (*.es, *.es6, *.cjs, *.mjs, *.jsm)\t*.es;*.es6;*.cjs;*.mjs;*.jsm\n" + \ .. "Vue Templates (*.vue)\t*.vue\n" + \ .. "JSON Files (*.json)\t*.json\n" + \ .. "All Files (*.*)\t*.*\n" endif - -let b:undo_ftplugin = "setl fo< ofu< com< cms<" + +" The following suffixes should be implied when resolving filenames +setlocal suffixesadd+=.js,.jsx,.es,.es6,.cjs,.mjs,.jsm,.vue,.json + +" The following suffixes should have low priority +" .snap jest snapshot +setlocal suffixes+=.snap + +" Remove irrelevant part of 'path'. +" User is expected to augment it with contextually-relevant paths +setlocal path-=/usr/include + +" Matchit configuration +if exists("loaded_matchit") + let b:match_ignorecase = 0 + let b:match_words = + \ '\<do\>:\<while\>,' + \ .. '<\@<=\([^ \t>/]\+\)\%(\s\+[^>]*\%([^/]>\|$\)\|>\|$\):<\@<=/\1>,' + \ .. '<\@<=\%([^ \t>/]\+\)\%(\s\+[^/>]*\|$\):/>' +endif + +" Set 'define' to a comprehensive value +let &l:define = + \ '\(^\s*(*async\s\+function\|(*function\)' + \ .. '\|^\s*\(\*\|static\|async\|get\|set\|\i\+\.\)' + \ .. '\|^\s*\(\ze\i\+\)\(([^)]*).*{$\|\s*[:=,]\)' + \ .. '\|^\s*\(export\s\+\|export\s\+default\s\+\)*\(var\|let\|const\|function\|class\)' + \ .. '\|\<as\>' + +let b:undo_ftplugin = + \ "setl fo< ofu< com< cms< sua< su< def< pa<" + \ .. "| unlet! b:browsefilter b:match_ignorecase b:match_words" let &cpo = s:cpo_save unlet s:cpo_save + +" vim: textwidth=78 tabstop=8 shiftwidth=4 softtabstop=4 expandtab diff --git a/runtime/ftplugin/javascriptreact.vim b/runtime/ftplugin/javascriptreact.vim index af318a01d6..b9c39d5e68 100644 --- a/runtime/ftplugin/javascriptreact.vim +++ b/runtime/ftplugin/javascriptreact.vim @@ -1,2 +1,7 @@ +" Vim filetype plugin file +" Language: JavaScript React +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2020 Aug 19 + " Placeholder for backwards compatilibity: .jsx used to stand for JavaScript. runtime! ftplugin/javascript.vim diff --git a/runtime/indent/elm.vim b/runtime/indent/elm.vim new file mode 100644 index 0000000000..232c347c66 --- /dev/null +++ b/runtime/indent/elm.vim @@ -0,0 +1,114 @@ +" Elm indent plugin file +" Language: Elm +" Maintainer: Andreas Scharf <as@99n.de> +" Original Author: Joseph Hager <ajhager@gmail.com> +" Copyright: Joseph Hager <ajhager@gmail.com> +" License: BSD3 +" Latest Revision: 2020-05-29 + +" Only load this indent file when no other was loaded. +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +" Local defaults +setlocal expandtab +setlocal indentexpr=GetElmIndent() +setlocal indentkeys+=0=else,0=if,0=of,0=import,0=then,0=type,0\|,0},0\],0),=-},0=in +setlocal nolisp +setlocal nosmartindent + +" Only define the function once. +if exists('*GetElmIndent') + finish +endif + +" Indent pairs +function! s:FindPair(pstart, pmid, pend) + "call search(a:pend, 'bW') + return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')) +endfunction + +function! GetElmIndent() + let l:lnum = v:lnum - 1 + + " Ident 0 if the first line of the file: + if l:lnum == 0 + return 0 + endif + + let l:ind = indent(l:lnum) + let l:lline = getline(l:lnum) + let l:line = getline(v:lnum) + + " Indent if current line begins with '}': + if l:line =~? '^\s*}' + return s:FindPair('{', '', '}') + + " Indent if current line begins with 'else': + elseif l:line =~# '^\s*else\>' + if l:lline !~# '^\s*\(if\|then\)\>' + return s:FindPair('\<if\>', '', '\<else\>') + endif + + " Indent if current line begins with 'then': + elseif l:line =~# '^\s*then\>' + if l:lline !~# '^\s*\(if\|else\)\>' + return s:FindPair('\<if\>', '', '\<then\>') + endif + + " HACK: Indent lines in case with nearest case clause: + elseif l:line =~# '->' && l:line !~# ':' && l:line !~# '\\' + return indent(search('^\s*case', 'bWn')) + &shiftwidth + + " HACK: Don't change the indentation if the last line is a comment. + elseif l:lline =~# '^\s*--' + return l:ind + + " Align the end of block comments with the start + elseif l:line =~# '^\s*-}' + return indent(search('{-', 'bWn')) + + " Indent double shift after let with an empty rhs + elseif l:lline =~# '\<let\>.*\s=$' + return l:ind + 4 + &shiftwidth + + " Align 'in' with the parent let. + elseif l:line =~# '^\s*in\>' + return indent(search('^\s*let', 'bWn')) + + " Align bindings with the parent let. + elseif l:lline =~# '\<let\>' + return l:ind + 4 + + " Align bindings with the parent in. + elseif l:lline =~# '^\s*in\>' + return l:ind + + endif + + " Add a 'shiftwidth' after lines ending with: + if l:lline =~# '\(|\|=\|->\|<-\|(\|\[\|{\|\<\(of\|else\|if\|then\)\)\s*$' + let l:ind = l:ind + &shiftwidth + + " Add a 'shiftwidth' after lines starting with type ending with '=': + elseif l:lline =~# '^\s*type' && l:line =~# '^\s*=' + let l:ind = l:ind + &shiftwidth + + " Back to normal indent after comments: + elseif l:lline =~# '-}\s*$' + call search('-}', 'bW') + let l:ind = indent(searchpair('{-', '', '-}', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')) + + " Ident some operators if there aren't any starting the last line. + elseif l:line =~# '^\s*\(!\|&\|(\|`\|+\||\|{\|[\|,\)=' && l:lline !~# '^\s*\(!\|&\|(\|`\|+\||\|{\|[\|,\)=' && l:lline !~# '^\s*$' + let l:ind = l:ind + &shiftwidth + + elseif l:lline ==# '' && getline(l:lnum - 1) !=# '' + let l:ind = indent(search('^\s*\S+', 'bWn')) + + endif + + return l:ind +endfunc diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index f082fe29f2..0326550245 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1,3 +1,5 @@ +local if_nil = vim.F.if_nil + local default_handlers = require 'vim.lsp.handlers' local log = require 'vim.lsp.log' local lsp_rpc = require 'vim.lsp.rpc' @@ -226,6 +228,7 @@ local function validate_client_config(config) on_init = { config.on_init, "f", true }; before_init = { config.before_init, "f", true }; offset_encoding = { config.offset_encoding, "s", true }; + flags = { config.flags, "t", true }; } -- TODO(remove-callbacks) @@ -434,6 +437,17 @@ end --- --@param trace: "off" | "messages" | "verbose" | nil passed directly to the language --- server in the initialize request. Invalid/empty values will default to "off" +--@param flags: A table with flags for the client. The current (experimental) flags are: +--- - allow_incremental_sync (bool, default false): Allow using on_line callbacks for lsp +--- +--- <pre> +--- -- In attach function for the client, you can do: +--- local custom_attach = function(client) +--- if client.config.flags then +--- client.config.flags.allow_incremental_sync = true +--- end +--- end +--- </pre> --- --@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be --- fully initialized. Use `on_init` to do any actions once @@ -442,6 +456,8 @@ function lsp.start_client(config) local cleaned_config = validate_client_config(config) local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding + config.flags = config.flags or {} + local client_id = next_client_id() -- TODO(remove-callbacks) @@ -553,6 +569,8 @@ function lsp.start_client(config) -- TODO(remove-callbacks) callbacks = handlers; handlers = handlers; + -- for $/progress report + messages = { name = name, messages = {}, progress = {}, status = {} } } -- Store the uninitialized_clients for cleanup in case we exit before initialize finishes. @@ -799,6 +817,7 @@ do local size_index = encoding_index[client.offset_encoding] local length = select(size_index, old_byte_size, old_utf16_size, old_utf32_size) local lines = nvim_buf_get_lines(bufnr, firstline, new_lastline, true) + -- This is necessary because we are specifying the full line including the -- newline in range. Therefore, we must replace the newline as well. if #lines > 0 then @@ -820,6 +839,8 @@ do end) local uri = vim.uri_from_bufnr(bufnr) for_each_buffer_client(bufnr, function(client, _client_id) + local allow_incremental_sync = if_nil(client.config.flags.allow_incremental_sync, false) + local text_document_did_change = client.resolved_capabilities.text_document_did_change local changes if text_document_did_change == protocol.TextDocumentSyncKind.None then @@ -830,7 +851,7 @@ do -- is no way to specify the sync capability by the client. -- See https://github.com/palantir/python-language-server/commit/cfd6675bc10d5e8dbc50fc50f90e4a37b7178821#diff-f68667852a14e9f761f6ebf07ba02fc8 for an example of pyls handling both. --]=] - elseif true or text_document_did_change == protocol.TextDocumentSyncKind.Full then + elseif not allow_incremental_sync or text_document_did_change == protocol.TextDocumentSyncKind.Full then changes = full_changes(client) elseif text_document_did_change == protocol.TextDocumentSyncKind.Incremental then changes = incremental_changes(client) @@ -862,8 +883,8 @@ function lsp._text_document_did_save_handler(bufnr) client.notify('textDocument/didSave', { textDocument = { uri = uri; - text = included_text; - } + }; + text = included_text; }) end end) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index a70581478b..00219b6d98 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -149,7 +149,7 @@ end --@param options Table with valid `FormattingOptions` entries. --@param start_pos ({number, number}, optional) mark-indexed position. ---Defaults to the start of the last visual selection. ---@param start_pos ({number, number}, optional) mark-indexed position. +--@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_formatting(options, start_pos, end_pos) validate { options = {options, 't', true} } @@ -239,6 +239,7 @@ function M.outgoing_calls() end --- List workspace folders. +--- function M.list_workspace_folders() local workspace_folders = {} for _, client in ipairs(vim.lsp.buf_get_clients()) do @@ -249,7 +250,8 @@ function M.list_workspace_folders() return workspace_folders end ---- Add a workspace folder. +--- Add the folder at path to the workspace folders. If {path} is +--- not provided, the user will be prompted for a path using |input()|. function M.add_workspace_folder(workspace_folder) workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h')) vim.api.nvim_command("redraw") @@ -275,7 +277,9 @@ function M.add_workspace_folder(workspace_folder) end end ---- Remove a workspace folder. +--- Remove the folder at path from the workspace folders. If +--- {path} is not provided, the user will be prompted for +--- a path using |input()|. function M.remove_workspace_folder(workspace_folder) workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h')) vim.api.nvim_command("redraw") diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 27a1f53f89..072349b226 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -400,9 +400,9 @@ end --- let sl = '' --- if luaeval('not vim.tbl_isempty(vim.lsp.buf_get_clients(0))') --- let sl.='%#MyStatuslineLSP#E:' ---- let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.diagnostic.get_count([[Error]])")}' +--- let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.diagnostic.get_count(0, [[Error]])")}' --- let sl.='%#MyStatuslineLSP# W:' ---- let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.diagnostic.get_count([[Warning]])")}' +--- let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.diagnostic.get_count(0, [[Warning]])")}' --- else --- let sl.='%#MyStatuslineLSPErrors#off' --- endif @@ -510,7 +510,7 @@ end --- Get the previous diagnostic closest to the cursor_position --- ----@param opts table See |vim.lsp.diagnostics.goto_next()| +---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic function M.get_prev(opts) opts = opts or {} @@ -523,7 +523,7 @@ function M.get_prev(opts) end --- Return the pos, {row, col}, for the prev diagnostic in the current buffer. ----@param opts table See |vim.lsp.diagnostics.goto_next()| +---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic position function M.get_prev_pos(opts) return _iter_diagnostic_lines_pos( @@ -533,7 +533,7 @@ function M.get_prev_pos(opts) end --- Move to the previous diagnostic ----@param opts table See |vim.lsp.diagnostics.goto_next()| +---@param opts table See |vim.lsp.diagnostic.goto_next()| function M.goto_prev(opts) return _iter_diagnostic_move_pos( "DiagnosticPrevious", @@ -543,7 +543,7 @@ function M.goto_prev(opts) end --- Get the previous diagnostic closest to the cursor_position ----@param opts table See |vim.lsp.diagnostics.goto_next()| +---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic function M.get_next(opts) opts = opts or {} @@ -556,7 +556,7 @@ function M.get_next(opts) end --- Return the pos, {row, col}, for the next diagnostic in the current buffer. ----@param opts table See |vim.lsp.diagnostics.goto_next()| +---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic position function M.get_next_pos(opts) return _iter_diagnostic_lines_pos( @@ -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 User LspDiagnosticsChanged") end -- }}} -- Diagnostic User Functions {{{ diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index e034923afb..87f35363b1 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -24,6 +24,79 @@ M['workspace/executeCommand'] = function(err, _) end end +-- @msg of type ProgressParams +-- Basically a token of type number/string +local function progress_callback(_, _, params, client_id) + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] client has shut down after sending the message") + end + local val = params.value -- unspecified yet + local token = params.token -- string or number + + + if val.kind then + if val.kind == 'begin' then + client.messages.progress[token] = { + title = val.title, + message = val.message, + percentage = val.percentage, + } + elseif val.kind == 'report' then + client.messages.progress[token].message = val.message; + client.messages.progress[token].percentage = val.percentage; + elseif val.kind == 'end' then + if client.messages.progress[token] == nil then + err_message("LSP[", client_name, "] received `end` message with no corresponding `begin`") + else + client.messages.progress[token].message = val.message + client.messages.progress[token].done = true + end + end + else + table.insert(client.messages, {content = val, show_once = true, shown = 0}) + end + + 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 + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] 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 + return actions[choice] + 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 @@ -72,6 +145,31 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit) } end +--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration +M['workspace/configuration'] = function(err, _, params, client_id) + local client = vim.lsp.get_client_by_id(client_id) + if not client then + err_message("LSP[id=", client_id, "] client has shut down after sending the message") + end + if err then error(vim.inspect(err)) end + if not params.items then + return {} + end + + local result = {} + for _, item in ipairs(params.items) do + if item.section then + local value = util.lookup_section(client.config.settings, item.section) or vim.NIL + -- For empty sections with no explicit '' key, return settings as is + if value == vim.NIL and item.section == '' then + value = client.config.settings or vim.NIL + end + table.insert(result, value) + end + end + return result +end + M['textDocument/publishDiagnostics'] = function(...) return require('vim.lsp.diagnostic').on_publish_diagnostics(...) end @@ -212,9 +310,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 218424fa14..3e111c154a 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -34,6 +34,13 @@ local constants = { Hint = 4; }; + DiagnosticTag = { + -- Unused or unnecessary code + Unnecessary = 1; + -- Deprecated or obsolete code + Deprecated = 2; + }; + MessageType = { -- An error message. Error = 1; @@ -292,8 +299,9 @@ local constants = { } for k, v in pairs(constants) do - vim.tbl_add_reverse_lookup(v) - protocol[k] = v + local tbl = vim.deepcopy(v) + vim.tbl_add_reverse_lookup(tbl) + protocol[k] = tbl end --[=[ @@ -520,6 +528,13 @@ export interface TextDocumentClientCapabilities { publishDiagnostics?: { --Whether the clients accepts diagnostics with related information. relatedInformation?: boolean; + --Client supports the tag property to provide meta data about a diagnostic. + --Clients supporting tags have to handle unknown tags gracefully. + --Since 3.15.0 + tagSupport?: { + --The tags supported by this client + valueSet: DiagnosticTag[]; + }; }; --Capabilities specific to `textDocument/foldingRange` requests. -- @@ -623,7 +638,11 @@ function protocol.make_client_capabilities() codeActionLiteralSupport = { codeActionKind = { - valueSet = vim.tbl_values(protocol.CodeActionKind); + valueSet = (function() + local res = vim.tbl_values(protocol.CodeActionKind) + table.sort(res) + return res + end)(); }; }; }; @@ -643,7 +662,7 @@ function protocol.make_client_capabilities() completionItemKind = { valueSet = (function() local res = {} - for k in pairs(protocol.CompletionItemKind) do + for k in ipairs(protocol.CompletionItemKind) do if type(k) == 'number' then table.insert(res, k) end end return res @@ -689,7 +708,7 @@ function protocol.make_client_capabilities() symbolKind = { valueSet = (function() local res = {} - for k in pairs(protocol.SymbolKind) do + for k in ipairs(protocol.SymbolKind) do if type(k) == 'number' then table.insert(res, k) end end return res @@ -701,6 +720,18 @@ function protocol.make_client_capabilities() dynamicRegistration = false; prepareSupport = true; }; + publishDiagnostics = { + relatedInformation = true; + tagSupport = { + valueSet = (function() + local res = {} + for k in ipairs(protocol.DiagnosticTag) do + if type(k) == 'number' then table.insert(res, k) end + end + return res + end)(); + }; + }; }; workspace = { symbol = { @@ -708,7 +739,7 @@ function protocol.make_client_capabilities() symbolKind = { valueSet = (function() local res = {} - for k in pairs(protocol.SymbolKind) do + for k in ipairs(protocol.SymbolKind) do if type(k) == 'number' then table.insert(res, k) end end return res @@ -723,6 +754,17 @@ function protocol.make_client_capabilities() dynamicRegistration = false; }; experimental = nil; + window = { + workDoneProgress = true; + showMessage = { + messageActionItem = { + additionalPropertiesSupport = false; + }; + }; + showDocument = { + support = false; + }; + }; } end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 5804ac6656..ecff95f61e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -120,6 +120,63 @@ local function get_line_byte_from_position(bufnr, position) return col end +--- Process and return progress reports from lsp server +function M.get_progress_messages() + + local new_messages = {} + local msg_remove = {} + local progress_remove = {} + + for _, client in ipairs(vim.lsp.get_active_clients()) do + local messages = client.messages + local data = messages + for token, ctx in pairs(data.progress) do + + local new_report = { + name = data.name, + title = ctx.title or "empty title", + message = ctx.message, + percentage = ctx.percentage, + progress = true, + } + table.insert(new_messages, new_report) + + if ctx.done then + table.insert(progress_remove, {client = client, token = token}) + end + end + + for i, msg in ipairs(data.messages) do + if msg.show_once then + msg.shown = msg.shown + 1 + if msg.shown > 1 then + table.insert(msg_remove, {client = client, idx = i}) + end + end + + table.insert(new_messages, {name = data.name, content = msg.content}) + end + + if next(data.status) ~= nil then + table.insert(new_messages, { + name = data.name, + content = data.status.content, + uri = data.status.uri, + status = true + }) + end + for _, item in ipairs(msg_remove) do + table.remove(client.messages, item.idx) + end + + for _, item in ipairs(progress_remove) do + client.messages.progress[item.token] = nil + end + end + + return new_messages +end + --- Applies a list of text edits to a buffer. --@param text_edits (table) list of `TextEdit` objects --@param buf_nr (number) Buffer id @@ -1022,7 +1079,7 @@ do --@deprecated function M.buf_diagnostics_signs(bufnr, diagnostics, client_id) - warn_once("buf_diagnostics_signs is deprecated. Use 'vim.lsp.diagnostics.set_signs'") + warn_once("buf_diagnostics_signs is deprecated. Use 'vim.lsp.diagnostic.set_signs'") return vim.lsp.diagnostic.set_signs(diagnostics, bufnr, client_id) end @@ -1315,6 +1372,9 @@ function M.make_text_document_params() return { uri = vim.uri_from_bufnr(0) } end +--- Create the workspace params +--@param added +--@param removed function M.make_workspace_params(added, removed) return { event = { added = added; removed = removed; } } end @@ -1362,6 +1422,21 @@ function M.character_offset(buf, row, col) return str_utfindex(line, col) end +--- Helper function to return nested values in language server settings +--- +--@param settings a table of language server settings +--@param section a string indicating the field of the settings table +--@returns (table or string) The value of settings accessed via section +function M.lookup_section(settings, section) + for part in vim.gsplit(section, '.', true) do + settings = settings[part] + if not settings then + return + end + end + return settings +end + M._get_line_byte_from_position = get_line_byte_from_position M._warn_once = warn_once diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 6886f0c178..79dcf77f9e 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -50,7 +50,7 @@ function M._create_parser(bufnr, lang, opts) end end - a.nvim_buf_attach(self.bufnr, false, {on_bytes=bytes_cb, on_detach=detach_cb}) + a.nvim_buf_attach(self.bufnr, false, {on_bytes=bytes_cb, on_detach=detach_cb, preview=true}) self:parse() diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index a7e36a0b89..d60cd2d0c7 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -8,7 +8,8 @@ local M = {} -- -- @param lang The language the parser should parse -- @param path Optionnal path the parser is located at -function M.require_language(lang, path) +-- @param silent Don't throw an error if language not found +function M.require_language(lang, path, silent) if vim._ts_has_language(lang) then return true end @@ -16,12 +17,23 @@ function M.require_language(lang, path) local fname = 'parser/' .. lang .. '.*' local paths = a.nvim_get_runtime_file(fname, false) if #paths == 0 then + if silent then + return false + end + -- TODO(bfredl): help tag? error("no parser for '"..lang.."' language, see :help treesitter-parsers") end path = paths[1] end - vim._ts_add_language(path, lang) + + if silent then + return pcall(function() vim._ts_add_language(path, lang) end) + else + vim._ts_add_language(path, lang) + end + + return true end --- Inspects the provided language. diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index a8b62e21b9..9c620c422c 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -121,23 +121,30 @@ function LanguageTree:parse() local seen_langs = {} for lang, injection_ranges in pairs(injections_by_lang) do - local child = self._children[lang] + local has_lang = language.require_language(lang, nil, true) - if not child then - child = self:add_child(lang) - end + -- Child language trees should just be ignored if not found, since + -- they can depend on the text of a node. Intermediate strings + -- would cause errors for unknown parsers. + if has_lang then + local child = self._children[lang] - child:set_included_regions(injection_ranges) + if not child then + child = self:add_child(lang) + end - local _, child_changes = child:parse() + child:set_included_regions(injection_ranges) - -- Propagate any child changes so they are included in the - -- the change list for the callback. - if child_changes then - vim.list_extend(changes, child_changes) - end + local _, child_changes = child:parse() - seen_langs[lang] = true + -- Propagate any child changes so they are included in the + -- the change list for the callback. + if child_changes then + vim.list_extend(changes, child_changes) + end + + seen_langs[lang] = true + end end for lang, _ in pairs(self._children) do @@ -282,7 +289,7 @@ function LanguageTree:_get_injections() local root_node = tree:root() local start_line, _, end_line, _ = root_node:range() - for pattern, match in self._injection_query:iter_matches(root_node, self._source, start_line, end_line+1) do + for pattern, match, metadata in self._injection_query:iter_matches(root_node, self._source, start_line, end_line+1) do local lang = nil local injection_node = nil local combined = false @@ -291,9 +298,9 @@ function LanguageTree:_get_injections() -- using a tag with the language, for example -- @javascript for id, node in pairs(match) do + local data = metadata[id] local name = self._injection_query.captures[id] - -- TODO add a way to offset the content passed to the parser. - -- Needed to shave off leading quotes and things of that nature. + local offset_range = data and data.offset -- Lang should override any other language tag if name == "language" then @@ -301,7 +308,7 @@ function LanguageTree:_get_injections() elseif name == "combined" then combined = true elseif name == "content" then - injection_node = node + injection_node = offset_range or node -- Ignore any tags that start with "_" -- Allows for other tags to be used in matches elseif string.sub(name, 1, 1) ~= "_" then @@ -310,7 +317,7 @@ function LanguageTree:_get_injections() end if not injection_node then - injection_node = node + injection_node = offset_range or node end end end @@ -445,7 +452,7 @@ end function LanguageTree:language_for_range(range) for _, child in pairs(self._children) do if child:contains(range) then - return child:node_for_range(range) + return child:language_for_range(range) end end diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 3537ba78f5..5a27d740a2 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -8,36 +8,10 @@ Query.__index = Query local M = {} --- Filter the runtime query files, the spec is like regular runtime files but in the new `queries` --- directory. They resemble ftplugins, that is that you can override queries by adding things in the --- `queries` directory, and extend using the `after/queries` directory. -local function filter_files(file_list) - local main = nil - local after = {} - - for _, fname in ipairs(file_list) do - -- Only get the name of the directory containing the queries directory - if vim.fn.fnamemodify(fname, ":p:h:h:h:t") == "after" then - table.insert(after, fname) - -- The first one is the one with most priority - elseif not main then - main = fname - end - end - - return main and { main, unpack(after) } or after -end - -local function runtime_query_path(lang, query_name) - return string.format('queries/%s/%s.scm', lang, query_name) -end - -local function filtered_runtime_queries(lang, query_name) - return filter_files(a.nvim_get_runtime_file(runtime_query_path(lang, query_name), true) or {}) -end -local function get_query_files(lang, query_name, is_included) - local lang_files = filtered_runtime_queries(lang, query_name) +function M.get_query_files(lang, query_name, is_included) + local query_path = string.format('queries/%s/%s.scm', lang, query_name) + local lang_files = a.nvim_get_runtime_file(query_path, true) if #lang_files == 0 then return {} end @@ -51,10 +25,10 @@ local function get_query_files(lang, query_name, is_included) local MODELINE_FORMAT = "^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$" for _, file in ipairs(lang_files) do - local modeline = vim.fn.readfile(file, "", 1) + local modeline = io.open(file, 'r'):read('*l') - if #modeline == 1 then - local langlist = modeline[1]:match(MODELINE_FORMAT) + if modeline then + local langlist = modeline:match(MODELINE_FORMAT) if langlist then for _, incllang in ipairs(vim.split(langlist, ',', true)) do @@ -74,7 +48,7 @@ local function get_query_files(lang, query_name, is_included) local query_files = {} for _, base_lang in ipairs(base_langs) do - local base_files = get_query_files(base_lang, query_name, true) + local base_files = M.get_query_files(base_lang, query_name, true) vim.list_extend(query_files, base_files) end vim.list_extend(query_files, lang_files) @@ -86,10 +60,21 @@ local function read_query_files(filenames) local contents = {} for _,filename in ipairs(filenames) do - vim.list_extend(contents, vim.fn.readfile(filename)) + table.insert(contents, io.open(filename, 'r'):read('*a')) end - return table.concat(contents, '\n') + return table.concat(contents, '') +end + +local match_metatable = { + __index = function(tbl, key) + rawset(tbl, key, {}) + return tbl[key] + end +} + +local function new_match_metadata() + return setmetatable({}, match_metatable) end --- Returns the runtime query {query_name} for {lang}. @@ -99,7 +84,7 @@ end -- -- @return The corresponding query, parsed. function M.get_query(lang, query_name) - local query_files = get_query_files(lang, query_name) + local query_files = M.get_query_files(lang, query_name) local query_string = read_query_files(query_files) if #query_string > 0 then @@ -222,6 +207,44 @@ local predicate_handlers = { -- As we provide lua-match? also expose vim-match? predicate_handlers["vim-match?"] = predicate_handlers["match?"] + +-- Directives store metadata or perform side effects against a match. +-- Directives should always end with a `!`. +-- Directive handler receive the following arguments +-- (match, pattern, bufnr, predicate) +local directive_handlers = { + ["set!"] = function(_, _, _, pred, metadata) + if #pred == 4 then + -- (set! @capture "key" "value") + metadata[pred[2]][pred[3]] = pred[4] + else + -- (set! "key" "value") + metadata[pred[2]] = pred[3] + end + end, + -- Shifts the range of a node. + -- Example: (#offset! @_node 0 1 0 -1) + ["offset!"] = function(match, _, _, pred, metadata) + local offset_node = match[pred[2]] + local range = {offset_node:range()} + local start_row_offset = pred[3] or 0 + local start_col_offset = pred[4] or 0 + local end_row_offset = pred[5] or 0 + local end_col_offset = pred[6] or 0 + local key = pred[7] or "offset" + + range[1] = range[1] + start_row_offset + range[2] = range[2] + start_col_offset + range[3] = range[3] + end_row_offset + range[4] = range[4] + end_col_offset + + -- If this produces an invalid range, we just skip it. + if range[1] < range[3] or (range[1] == range[3] and range[2] <= range[4]) then + metadata[pred[2]][key] = range + end + end +} + --- Adds a new predicates to be used in queries -- -- @param name the name of the predicate, without leading # @@ -229,12 +252,25 @@ predicate_handlers["vim-match?"] = predicate_handlers["match?"] -- signature will be (match, pattern, bufnr, predicate) function M.add_predicate(name, handler, force) if predicate_handlers[name] and not force then - a.nvim_err_writeln(string.format("Overriding %s", name)) + error(string.format("Overriding %s", name)) end predicate_handlers[name] = handler end +--- Adds a new directive to be used in queries +-- +-- @param name the name of the directive, without leading # +-- @param handler the handler function to be used +-- signature will be (match, pattern, bufnr, predicate) +function M.add_directive(name, handler, force) + if directive_handlers[name] and not force then + error(string.format("Overriding %s", name)) + end + + directive_handlers[name] = handler +end + --- Returns the list of currently supported predicates function M.list_predicates() return vim.tbl_keys(predicate_handlers) @@ -244,6 +280,10 @@ local function xor(x, y) return (x or y) and not (x and y) end +local function is_directive(name) + return string.sub(name, -1) == "!" +end + function Query:match_preds(match, pattern, source) local preds = self.info.patterns[pattern] @@ -254,30 +294,52 @@ function Query:match_preds(match, pattern, source) -- Also, tree-sitter strips the leading # from predicates for us. local pred_name local is_not - if string.sub(pred[1], 1, 4) == "not-" then - pred_name = string.sub(pred[1], 5) - is_not = true - else - pred_name = pred[1] - is_not = false - end - local handler = predicate_handlers[pred_name] + -- Skip over directives... they will get processed after all the predicates. + if not is_directive(pred[1]) then + if string.sub(pred[1], 1, 4) == "not-" then + pred_name = string.sub(pred[1], 5) + is_not = true + else + pred_name = pred[1] + is_not = false + end + + local handler = predicate_handlers[pred_name] - if not handler then - a.nvim_err_writeln(string.format("No handler for %s", pred[1])) - return false - end + if not handler then + error(string.format("No handler for %s", pred[1])) + return false + end - local pred_matches = handler(match, pattern, source, pred) + local pred_matches = handler(match, pattern, source, pred) - if not xor(is_not, pred_matches) then - return false + if not xor(is_not, pred_matches) then + return false + end end end return true end +--- Applies directives against a match and pattern. +function Query:apply_directives(match, pattern, source, metadata) + local preds = self.info.patterns[pattern] + + for _, pred in pairs(preds or {}) do + if is_directive(pred[1]) then + local handler = directive_handlers[pred[1]] + + if not handler then + error(string.format("No handler for %s", pred[1])) + return + end + + handler(match, pattern, source, pred, metadata) + end + end +end + --- Iterates of the captures of self on a given range. -- -- @param node The node under witch the search will occur @@ -294,14 +356,18 @@ function Query:iter_captures(node, source, start, stop) local raw_iter = node:_rawquery(self.query, true, start, stop) local function iter() local capture, captured_node, match = raw_iter() + local metadata = new_match_metadata() + if match ~= nil then local active = self:match_preds(match, match.pattern, source) match.active = active if not active then return iter() -- tail call: try next match end + + self:apply_directives(match, match.pattern, source, metadata) end - return capture, captured_node + return capture, captured_node, metadata end return iter end @@ -322,13 +388,17 @@ function Query:iter_matches(node, source, start, stop) local raw_iter = node:_rawquery(self.query, false, start, stop) local function iter() local pattern, match = raw_iter() + local metadata = new_match_metadata() + if match ~= nil then local active = self:match_preds(match, pattern, source) if not active then return iter() -- tail call: try next match end + + self:apply_directives(match, pattern, source, metadata) end - return pattern, match + return pattern, match, metadata end return iter end diff --git a/runtime/plugin/man.vim b/runtime/plugin/man.vim index e762eb3664..689aa32ef3 100644 --- a/runtime/plugin/man.vim +++ b/runtime/plugin/man.vim @@ -5,9 +5,9 @@ if exists('g:loaded_man') endif let g:loaded_man = 1 -command! -bang -bar -range=0 -complete=customlist,man#complete -nargs=* Man +command! -bang -bar -range=-1 -complete=customlist,man#complete -nargs=* Man \ if <bang>0 | set ft=man | - \ else | call man#open_page(v:count, v:count1, <q-mods>, <f-args>) | endif + \ else | call man#open_page(<count>, <q-mods>, <f-args>) | endif augroup man autocmd! diff --git a/runtime/syntax/elm.vim b/runtime/syntax/elm.vim new file mode 100644 index 0000000000..1277827f57 --- /dev/null +++ b/runtime/syntax/elm.vim @@ -0,0 +1,105 @@ +" Vim syntax file +" Language: Elm +" Maintainer: Andreas Scharf <as@99n.de> +" Original Author: Joseph Hager <ajhager@gmail.com> +" Copyright: Joseph Hager <ajhager@gmail.com> +" License: BSD3 +" Latest Revision: 2020-05-29 + +if exists('b:current_syntax') + finish +endif + +" Keywords +syn keyword elmConditional else if of then case +syn keyword elmAlias alias +syn keyword elmTypedef contained type port +syn keyword elmImport exposing as import module where + +" Operators +" elm/core +syn match elmOperator contained "\(<|\||>\|||\|&&\|==\|/=\|<=\|>=\|++\|::\|+\|-\|*\|/\|//\|^\|<>\|>>\|<<\|<\|>\|%\)" +" elm/parser +syn match elmOperator contained "\(|.\||=\)" +" elm/url +syn match elmOperator contained "\(</>\|<?>\)" + +" Types +syn match elmType "\<[A-Z][0-9A-Za-z_-]*" +syn keyword elmNumberType number + +" Modules +syn match elmModule "\<\([A-Z][0-9A-Za-z_'-\.]*\)\+\.[A-Za-z]"me=e-2 +syn match elmModule "^\(module\|import\)\s\+[A-Z][0-9A-Za-z_'-\.]*\(\s\+as\s\+[A-Z][0-9A-Za-z_'-\.]*\)\?\(\s\+exposing\)\?" contains=elmImport + +" Delimiters +syn match elmDelimiter "[,;]" +syn match elmBraces "[()[\]{}]" + +" Functions +syn match elmTupleFunction "\((,\+)\)" + +" Comments +syn keyword elmTodo TODO FIXME XXX contained +syn match elmLineComment "--.*" contains=elmTodo,@spell +syn region elmComment matchgroup=elmComment start="{-|\=" end="-}" contains=elmTodo,elmComment,@spell fold + +" Strings +syn match elmStringEscape "\\u[0-9a-fA-F]\{4}" contained +syn match elmStringEscape "\\[nrfvbt\\\"]" contained +syn region elmString start="\"" skip="\\\"" end="\"" contains=elmStringEscape,@spell +syn region elmTripleString start="\"\"\"" skip="\\\"" end="\"\"\"" contains=elmStringEscape,@spell +syn match elmChar "'[^'\\]'\|'\\.'\|'\\u[0-9a-fA-F]\{4}'" + +" Lambda +syn region elmLambdaFunc start="\\"hs=s+1 end="->"he=e-2 + +" Debug +syn match elmDebug "Debug.\(log\|todo\|toString\)" + +" Numbers +syn match elmInt "-\?\<\d\+\>" +syn match elmFloat "-\?\(\<\d\+\.\d\+\>\)" + +" Identifiers +syn match elmTopLevelDecl "^\s*[a-zA-Z][a-zA-z0-9_]*\('\)*\s\+:\(\r\n\|\r\|\n\|\s\)\+" contains=elmOperator +syn match elmFuncName /^\l\w*/ + +" Folding +syn region elmTopLevelTypedef start="type" end="\n\(\n\n\)\@=" contains=ALL fold +syn region elmTopLevelFunction start="^[a-zA-Z].\+\n[a-zA-Z].\+=" end="^\(\n\+\)\@=" contains=ALL fold +syn region elmCaseBlock matchgroup=elmCaseBlockDefinition start="^\z\(\s\+\)\<case\>" end="^\z1\@!\W\@=" end="\(\n\n\z1\@!\)\@=" end="\n\z1\@!\(\n\n\)\@=" contains=ALL fold +syn region elmCaseItemBlock start="^\z\(\s\+\).\+->$" end="^\z1\@!\W\@=" end="\(\n\n\z1\@!\)\@=" end="\(\n\z1\S\)\@=" contains=ALL fold +syn region elmLetBlock matchgroup=elmLetBlockDefinition start="\<let\>" end="\<in\>" contains=ALL fold + +hi def link elmFuncName Function +hi def link elmCaseBlockDefinition Conditional +hi def link elmCaseBlockItemDefinition Conditional +hi def link elmLetBlockDefinition TypeDef +hi def link elmTopLevelDecl Function +hi def link elmTupleFunction Normal +hi def link elmTodo Todo +hi def link elmComment Comment +hi def link elmLineComment Comment +hi def link elmString String +hi def link elmTripleString String +hi def link elmChar String +hi def link elmStringEscape Special +hi def link elmInt Number +hi def link elmFloat Float +hi def link elmDelimiter Delimiter +hi def link elmBraces Delimiter +hi def link elmTypedef TypeDef +hi def link elmImport Include +hi def link elmConditional Conditional +hi def link elmAlias Delimiter +hi def link elmOperator Operator +hi def link elmType Type +hi def link elmNumberType Identifier +hi def link elmLambdaFunc Function +hi def link elmDebug Debug +hi def link elmModule Type + +syn sync minlines=500 + +let b:current_syntax = 'elm' diff --git a/runtime/syntax/javascript.vim b/runtime/syntax/javascript.vim index 78714d0170..8a5e10b261 100644 --- a/runtime/syntax/javascript.vim +++ b/runtime/syntax/javascript.vim @@ -7,7 +7,7 @@ " (ss) repaired several quoting and grouping glitches " (ss) fixed regex parsing issue with multiple qualifiers [gi] " (ss) additional factoring of keywords, globals, and members -" Last Change: 2018 Jul 28 +" Last Change: 2020 May 14 " 2013 Jun 12: adjusted javaScriptRegexpString (Kevin Locke) " 2018 Apr 14: adjusted javaScriptRegexpString (LongJohnCoder) @@ -48,7 +48,7 @@ syn keyword javaScriptRepeat while for do in syn keyword javaScriptBranch break continue syn keyword javaScriptOperator new delete instanceof typeof syn keyword javaScriptType Array Boolean Date Function Number Object String RegExp -syn keyword javaScriptStatement return with +syn keyword javaScriptStatement return with await syn keyword javaScriptBoolean true false syn keyword javaScriptNull null undefined syn keyword javaScriptIdentifier arguments this var let @@ -58,7 +58,7 @@ syn keyword javaScriptMessage alert confirm prompt status syn keyword javaScriptGlobal self window top parent syn keyword javaScriptMember document event location syn keyword javaScriptDeprecated escape unescape -syn keyword javaScriptReserved abstract boolean byte char class const debugger double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile +syn keyword javaScriptReserved abstract boolean byte char class const debugger double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile async syn cluster javaScriptEmbededExpr contains=javaScriptBoolean,javaScriptNull,javaScriptIdentifier,javaScriptStringD,javaScriptStringS,javaScriptStringT @@ -77,10 +77,10 @@ else syn match javaScriptParens "[()]" endif -syn sync fromstart -syn sync maxlines=100 - if main_syntax == "javascript" + syn sync fromstart + syn sync maxlines=100 + syn sync ccomment javaScriptComment endif diff --git a/runtime/syntax/javascriptreact.vim b/runtime/syntax/javascriptreact.vim index fc49691cb0..0067a0b352 100644 --- a/runtime/syntax/javascriptreact.vim +++ b/runtime/syntax/javascriptreact.vim @@ -1,2 +1,3 @@ -" Placeholder for backwards compatilibity: .jsx used to stand for JavaScript. +" Placeholder for backwards compatilibity: .jsx used to be associated with the +" filetpye JavaScript. runtime! syntax/javascript.vim diff --git a/runtime/syntax/php.vim b/runtime/syntax/php.vim index 1c5c24e56b..30d13581c3 100644 --- a/runtime/syntax/php.vim +++ b/runtime/syntax/php.vim @@ -1,9 +1,11 @@ " Vim syntax file -" Language: php PHP 3/4/5/7 -" Maintainer: Jason Woofenden <jason@jasonwoof.com> -" Last Change: Jun 20, 2018 -" URL: https://jasonwoof.com/gitweb/?p=vim-syntax.git;a=blob;f=php.vim;hb=HEAD -" Former Maintainers: Peter Hodge <toomuchphp-vim@yahoo.com> +" Language: php PHP 3/4/5/7/8 +" Maintainer: Tyson Andre <tysonandre775@hotmail.com> +" Last Change: Sep 07, 2020 +" URL: https://github.com/TysonAndre/vim-syntax +" Former Maintainers: +" Jason Woofenden <jason@jasonwoof.com> +" Peter Hodge <toomuchphp-vim@yahoo.com> " Debian VIM Maintainers <pkg-vim-maintainers@lists.alioth.debian.org> " " Note: If you are using a colour terminal with dark background, you will @@ -107,7 +109,7 @@ syn keyword phpIntVar GLOBALS PHP_ERRMSG PHP_SELF HTTP_GET_VARS HTTP_POST_VARS H syn keyword phpCoreConstant PHP_VERSION PHP_OS DEFAULT_INCLUDE_PATH PEAR_INSTALL_DIR PEAR_EXTENSION_DIR PHP_EXTENSION_DIR PHP_BINDIR PHP_LIBDIR PHP_DATADIR PHP_SYSCONFDIR PHP_LOCALSTATEDIR PHP_CONFIG_FILE_PATH PHP_OUTPUT_HANDLER_START PHP_OUTPUT_HANDLER_CONT PHP_OUTPUT_HANDLER_END contained " Predefined constants -" Generated by: curl -q http://php.net/manual/en/errorfunc.constants.php | grep -oP 'E_\w+' | sort -u +" Generated by: curl -q https://www.php.net/manual/en/errorfunc.constants.php | grep -oP 'E_\w+' | sort -u syn keyword phpCoreConstant E_ALL E_COMPILE_ERROR E_COMPILE_WARNING E_CORE_ERROR E_CORE_WARNING E_DEPRECATED E_ERROR E_NOTICE E_PARSE E_RECOVERABLE_ERROR E_STRICT E_USER_DEPRECATED E_USER_ERROR E_USER_NOTICE E_USER_WARNING E_WARNING contained syn case ignore @@ -115,7 +117,10 @@ syn case ignore syn keyword phpConstant __LINE__ __FILE__ __FUNCTION__ __METHOD__ __CLASS__ __DIR__ __NAMESPACE__ __TRAIT__ contained -" Function and Methods ripped from php_manual_de.tar.gz Jan 2003 +" Function and Methods ripped from php_manual_de.tar.gz Jan 2003 and amended later with common bundled extensions + +syn keyword phpFunctions apc_cache_info apc_clear_cache apc_store apc_fetch apc_enabled apc_delete apc_add apc_sma_info apc_inc apc_dec apc_cas apc_exists contained +syn keyword phpFunctions apcu_add apcu_cache_info apcu_cas apcu_clear_cache apcu_dec apcu_delete apcu_enabled apcu_entry apcu_exists apcu_fetch apcu_inc apcu_key_info apcu_sma_info apcu_store contained syn keyword phpFunctions apache_child_terminate apache_get_modules apache_get_version apache_getenv apache_lookup_uri apache_note apache_request_headers apache_response_headers apache_setenv ascii2ebcdic ebcdic2ascii getallheaders virtual contained syn keyword phpFunctions array_change_key_case array_chunk array_column array_combine array_count_values array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_diff array_fill_keys array_fill array_filter array_flip array_intersect_assoc array_intersect_key array_intersect_uassoc array_intersect_ukey array_intersect array_key_exists array_keys array_map array_merge_recursive array_merge array_multisort array_pad array_pop array_product array_push array_rand array_reduce array_replace_recursive array_replace array_reverse array_search array_shift array_slice array_splice array_sum array_udiff_assoc array_udiff_uassoc array_udiff array_uintersect_assoc array_uintersect_uassoc array_uintersect array_unique array_unshift array_values array_walk_recursive array_walk arsort asort count current each end in_array key_exists key krsort ksort natcasesort natsort next pos prev range reset rsort shuffle sizeof sort uasort uksort usort contained syn keyword phpFunctions aspell_check aspell_new aspell_suggest contained @@ -128,10 +133,10 @@ syn keyword phpFunctions com VARIANT com_addref com_get com_invoke com_isenum co syn keyword phpFunctions cpdf_add_annotation cpdf_add_outline cpdf_arc cpdf_begin_text cpdf_circle cpdf_clip cpdf_close cpdf_closepath_fill_stroke cpdf_closepath_stroke cpdf_closepath cpdf_continue_text cpdf_curveto cpdf_end_text cpdf_fill_stroke cpdf_fill cpdf_finalize_page cpdf_finalize cpdf_global_set_document_limits cpdf_import_jpeg cpdf_lineto cpdf_moveto cpdf_newpath cpdf_open cpdf_output_buffer cpdf_page_init cpdf_place_inline_image cpdf_rect cpdf_restore cpdf_rlineto cpdf_rmoveto cpdf_rotate_text cpdf_rotate cpdf_save_to_file cpdf_save cpdf_scale cpdf_set_action_url cpdf_set_char_spacing cpdf_set_creator cpdf_set_current_page cpdf_set_font_directories cpdf_set_font_map_file cpdf_set_font cpdf_set_horiz_scaling cpdf_set_keywords cpdf_set_leading cpdf_set_page_animation cpdf_set_subject cpdf_set_text_matrix cpdf_set_text_pos cpdf_set_text_rendering cpdf_set_text_rise cpdf_set_title cpdf_set_viewer_preferences cpdf_set_word_spacing cpdf_setdash cpdf_setflat cpdf_setgray_fill cpdf_setgray_stroke cpdf_setgray cpdf_setlinecap cpdf_setlinejoin cpdf_setlinewidth cpdf_setmiterlimit cpdf_setrgbcolor_fill cpdf_setrgbcolor_stroke cpdf_setrgbcolor cpdf_show_xy cpdf_show cpdf_stringwidth cpdf_stroke cpdf_text cpdf_translate contained syn keyword phpFunctions crack_check crack_closedict crack_getlastmessage crack_opendict contained syn keyword phpFunctions ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_graph ctype_lower ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit contained -syn keyword phpFunctions curl_close curl_errno curl_error curl_exec curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_setopt curl_version contained +syn keyword phpFunctions curl_close curl_errno curl_error curl_exec curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_setopt curl_version curl_copy_handle curl_escape curl_file_create curl_multi_errno curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_share_close curl_share_errno curl_share_init curl_share_setopt curl_share_strerror curl_strerror curl_unescape contained syn keyword phpFunctions cybercash_base64_decode cybercash_base64_encode cybercash_decr cybercash_encr contained syn keyword phpFunctions cyrus_authenticate cyrus_bind cyrus_close cyrus_connect cyrus_query cyrus_unbind contained -syn keyword phpFunctions checkdate date getdate gettimeofday gmdate gmmktime gmstrftime localtime microtime mktime strftime strtotime time contained +syn keyword phpFunctions checkdate date getdate gettimeofday gmdate gmmktime gmstrftime localtime microtime mktime strftime strtotime time date_add date_create date_create_from_format date_create_immutable date_create_immutable_from_format date_date_set date_default_timezone_get date_default_timezone_set date_diff date_format date_get_last_errors date_interval_create_from_date_string date_interval_format date_isodate_set date_modify date_offset_get date_parse date_parse_from_format date_sub date_sun_info date_sunrise date_sunset date_time_set date_timestamp_get date_timestamp_set date_timezone_get date_timezone_set idate timezone_abbreviations_list timezone_identifiers_list timezone_location_get timezone_name_from_abbr timezone_name_get timezone_offset_get timezone_open timezone_transitions_get timezone_version_get contained syn keyword phpFunctions dba_close dba_delete dba_exists dba_fetch dba_firstkey dba_handlers dba_insert dba_key_split dba_list dba_nextkey dba_open dba_optimize dba_popen dba_replace dba_sync contained syn keyword phpFunctions dbase_add_record dbase_close dbase_create dbase_delete_record dbase_get_header_info dbase_get_record_with_names dbase_get_record dbase_numfields dbase_numrecords dbase_open dbase_pack dbase_replace_record contained syn keyword phpFunctions dblist dbmclose dbmdelete dbmexists dbmfetch dbmfirstkey dbminsert dbmnextkey dbmopen dbmreplace contained @@ -139,21 +144,25 @@ syn keyword phpFunctions dbplus_add dbplus_aql dbplus_chdir dbplus_close dbplus_ syn keyword phpFunctions dbx_close dbx_compare dbx_connect dbx_error dbx_escape_string dbx_fetch_row dbx_query dbx_sort contained syn keyword phpFunctions dio_close dio_fcntl dio_open dio_read dio_seek dio_stat dio_tcsetattr dio_truncate dio_write contained syn keyword phpFunctions chdir chroot dir closedir getcwd opendir readdir rewinddir scandir contained +syn keyword phpFunctions dom_import_simplexml contained syn keyword phpFunctions domxml_new_doc domxml_open_file domxml_open_mem domxml_version domxml_xmltree domxml_xslt_stylesheet_doc domxml_xslt_stylesheet_file domxml_xslt_stylesheet xpath_eval_expression xpath_eval xpath_new_context xptr_eval xptr_new_context contained syn keyword phpMethods name specified value create_attribute create_cdata_section create_comment create_element_ns create_element create_entity_reference create_processing_instruction create_text_node doctype document_element dump_file dump_mem get_element_by_id get_elements_by_tagname html_dump_mem xinclude entities internal_subset name notations public_id system_id get_attribute_node get_attribute get_elements_by_tagname has_attribute remove_attribute set_attribute tagname add_namespace append_child append_sibling attributes child_nodes clone_node dump_node first_child get_content has_attributes has_child_nodes insert_before is_blank_node last_child next_sibling node_name node_type node_value owner_document parent_node prefix previous_sibling remove_child replace_child replace_node set_content set_name set_namespace unlink_node data target process result_dump_file result_dump_mem contained syn keyword phpFunctions dotnet_load contained syn keyword phpFunctions debug_backtrace debug_print_backtrace error_log error_reporting restore_error_handler set_error_handler trigger_error user_error contained +syn keyword phpFunctions enchant_broker_describe enchant_broker_dict_exists enchant_broker_free enchant_broker_free_dict enchant_broker_get_dict_path enchant_broker_get_error enchant_broker_init enchant_broker_list_dicts enchant_broker_request_dict enchant_broker_request_pwl_dict enchant_broker_set_dict_path enchant_broker_set_ordering enchant_dict_add_to_personal enchant_dict_add_to_session enchant_dict_check enchant_dict_describe enchant_dict_get_error enchant_dict_is_in_session enchant_dict_quick_check enchant_dict_store_replacement enchant_dict_suggest contained syn keyword phpFunctions escapeshellarg escapeshellcmd exec passthru proc_close proc_get_status proc_nice proc_open proc_terminate shell_exec system contained syn keyword phpFunctions fam_cancel_monitor fam_close fam_monitor_collection fam_monitor_directory fam_monitor_file fam_next_event fam_open fam_pending fam_resume_monitor fam_suspend_monitor contained syn keyword phpFunctions fbsql_affected_rows fbsql_autocommit fbsql_change_user fbsql_close fbsql_commit fbsql_connect fbsql_create_blob fbsql_create_clob fbsql_create_db fbsql_data_seek fbsql_database_password fbsql_database fbsql_db_query fbsql_db_status fbsql_drop_db fbsql_errno fbsql_error fbsql_fetch_array fbsql_fetch_assoc fbsql_fetch_field fbsql_fetch_lengths fbsql_fetch_object fbsql_fetch_row fbsql_field_flags fbsql_field_len fbsql_field_name fbsql_field_seek fbsql_field_table fbsql_field_type fbsql_free_result fbsql_get_autostart_info fbsql_hostname fbsql_insert_id fbsql_list_dbs fbsql_list_fields fbsql_list_tables fbsql_next_result fbsql_num_fields fbsql_num_rows fbsql_password fbsql_pconnect fbsql_query fbsql_read_blob fbsql_read_clob fbsql_result fbsql_rollback fbsql_select_db fbsql_set_lob_mode fbsql_set_transaction fbsql_start_db fbsql_stop_db fbsql_tablename fbsql_username fbsql_warnings contained syn keyword phpFunctions fdf_add_doc_javascript fdf_add_template fdf_close fdf_create fdf_enum_values fdf_errno fdf_error fdf_get_ap fdf_get_attachment fdf_get_encoding fdf_get_file fdf_get_flags fdf_get_opt fdf_get_status fdf_get_value fdf_get_version fdf_header fdf_next_field_name fdf_open_string fdf_open fdf_remove_item fdf_save_string fdf_save fdf_set_ap fdf_set_encoding fdf_set_file fdf_set_flags fdf_set_javascript_action fdf_set_opt fdf_set_status fdf_set_submit_form_action fdf_set_target_frame fdf_set_value fdf_set_version contained syn keyword phpFunctions filepro_fieldcount filepro_fieldname filepro_fieldtype filepro_fieldwidth filepro_retrieve filepro_rowcount filepro contained +syn keyword phpFunctions filter_has_var filter_id filter_input filter_input_array filter_list filter_var filter_var_array contained syn keyword phpFunctions basename chgrp chmod chown clearstatcache copy delete dirname disk_free_space disk_total_space diskfreespace fclose feof fflush fgetc fgetcsv fgets fgetss file_exists file_get_contents file_put_contents file fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype flock fnmatch fopen fpassthru fputs fread fscanf fseek fstat ftell ftruncate fwrite glob is_dir is_executable is_file is_link is_readable is_uploaded_file is_writable is_writeable link linkinfo lstat mkdir move_uploaded_file parse_ini_file pathinfo pclose popen readfile readlink realpath rename rewind rmdir set_file_buffer stat symlink tempnam tmpfile touch umask unlink contained syn keyword phpFunctions fribidi_log2vis contained -syn keyword phpFunctions ftp_alloc ftp_cdup ftp_chdir ftp_chmod ftp_close ftp_connect ftp_delete ftp_exec ftp_fget ftp_fput ftp_get_option ftp_get ftp_login ftp_mdtm ftp_mkdir ftp_nb_continue ftp_nb_fget ftp_nb_fput ftp_nb_get ftp_nb_put ftp_nlist ftp_pasv ftp_put ftp_pwd ftp_quit ftp_raw ftp_rawlist ftp_rename ftp_rmdir ftp_set_option ftp_site ftp_size ftp_ssl_connect ftp_systype contained +syn keyword phpFunctions ftp_alloc ftp_cdup ftp_chdir ftp_chmod ftp_close ftp_connect ftp_delete ftp_exec ftp_fget ftp_fput ftp_get_option ftp_get ftp_login ftp_mdtm ftp_mkdir ftp_nb_continue ftp_nb_fget ftp_nb_fput ftp_nb_get ftp_nb_put ftp_nlist ftp_pasv ftp_put ftp_pwd ftp_quit ftp_raw ftp_rawlist ftp_rename ftp_rmdir ftp_set_option ftp_site ftp_size ftp_ssl_connect ftp_systype ftp_append ftp_mlsd contained syn keyword phpFunctions call_user_func_array call_user_func create_function func_get_arg func_get_args func_num_args function_exists get_defined_functions register_shutdown_function register_tick_function unregister_tick_function contained -syn keyword phpFunctions bind_textdomain_codeset bindtextdomain dcgettext dcngettext dgettext dngettext gettext ngettext textdomain contained -syn keyword phpFunctions gmp_abs gmp_add gmp_and gmp_clrbit gmp_cmp gmp_com gmp_div_q gmp_div_qr gmp_div_r gmp_div gmp_divexact gmp_fact gmp_gcd gmp_gcdext gmp_hamdist gmp_init gmp_intval gmp_invert gmp_jacobi gmp_legendre gmp_mod gmp_mul gmp_neg gmp_or gmp_perfect_square gmp_popcount gmp_pow gmp_powm gmp_prob_prime gmp_random gmp_scan0 gmp_scan1 gmp_setbit gmp_sign gmp_sqrt gmp_sqrtrem gmp_sqrtrm gmp_strval gmp_sub gmp_xor contained +syn keyword phpFunctions bind_textdomain_codeset bindtextdomain dcgettext dcngettext dgettext dngettext gettext ngettext textdomain _ contained +syn keyword phpFunctions gmp_abs gmp_add gmp_and gmp_clrbit gmp_cmp gmp_com gmp_div_q gmp_div_qr gmp_div_r gmp_div gmp_divexact gmp_fact gmp_gcd gmp_gcdext gmp_hamdist gmp_init gmp_intval gmp_invert gmp_jacobi gmp_legendre gmp_mod gmp_mul gmp_neg gmp_or gmp_perfect_square gmp_popcount gmp_pow gmp_powm gmp_prob_prime gmp_random gmp_scan0 gmp_scan1 gmp_setbit gmp_sign gmp_sqrt gmp_sqrtrem gmp_sqrtrm gmp_strval gmp_sub gmp_xor gmp_binomial gmp_export gmp_import gmp_kronecker gmp_lcm gmp_nextprime gmp_perfect_power gmp_random_bits gmp_random_range gmp_random_seed gmp_root gmp_rootrem gmp_testbit contained +syn keyword phpFunctions hash hash_algos hash_copy hash_equals hash_file hash_final hash_hkdf hash_hmac hash_hmac_algos hash_hmac_file hash_init hash_pbkdf2 hash_update hash_update_file hash_update_stream contained syn keyword phpFunctions header headers_list headers_sent setcookie contained syn keyword phpFunctions hw_api_attribute hwapi_hgcsp hw_api_content hw_api_object contained syn keyword phpMethods key langdepvalue value values checkin checkout children mimetype read content copy dbstat dcstat dstanchors dstofsrcanchors count reason find ftstat hwstat identify info insert insertanchor insertcollection insertdocument link lock move assign attreditable count insert remove title value object objectbyanchor parents description type remove replace setcommitedversion srcanchors srcsofdst unlock user userlist contained @@ -161,24 +170,29 @@ syn keyword phpFunctions hw_Array2Objrec hw_changeobject hw_Children hw_Children syn keyword phpFunctions ibase_add_user ibase_affected_rows ibase_blob_add ibase_blob_cancel ibase_blob_close ibase_blob_create ibase_blob_echo ibase_blob_get ibase_blob_import ibase_blob_info ibase_blob_open ibase_close ibase_commit_ret ibase_commit ibase_connect ibase_delete_user ibase_drop_db ibase_errcode ibase_errmsg ibase_execute ibase_fetch_assoc ibase_fetch_object ibase_fetch_row ibase_field_info ibase_free_event_handler ibase_free_query ibase_free_result ibase_gen_id ibase_modify_user ibase_name_result ibase_num_fields ibase_num_params ibase_param_info ibase_pconnect ibase_prepare ibase_query ibase_rollback_ret ibase_rollback ibase_set_event_handler ibase_timefmt ibase_trans ibase_wait_event contained syn keyword phpFunctions iconv_get_encoding iconv_mime_decode_headers iconv_mime_decode iconv_mime_encode iconv_set_encoding iconv_strlen iconv_strpos iconv_strrpos iconv_substr iconv ob_iconv_handler contained syn keyword phpFunctions ifx_affected_rows ifx_blobinfile_mode ifx_byteasvarchar ifx_close ifx_connect ifx_copy_blob ifx_create_blob ifx_create_char ifx_do ifx_error ifx_errormsg ifx_fetch_row ifx_fieldproperties ifx_fieldtypes ifx_free_blob ifx_free_char ifx_free_result ifx_get_blob ifx_get_char ifx_getsqlca ifx_htmltbl_result ifx_nullformat ifx_num_fields ifx_num_rows ifx_pconnect ifx_prepare ifx_query ifx_textasvarchar ifx_update_blob ifx_update_char ifxus_close_slob ifxus_create_slob ifxus_free_slob ifxus_open_slob ifxus_read_slob ifxus_seek_slob ifxus_tell_slob ifxus_write_slob contained -syn keyword phpFunctions exif_imagetype exif_read_data exif_thumbnail gd_info getimagesize image_type_to_mime_type image2wbmp imagealphablending imageantialias imagearc imagechar imagecharup imagecolorallocate imagecolorallocatealpha imagecolorat imagecolorclosest imagecolorclosestalpha imagecolorclosesthwb imagecolordeallocate imagecolorexact imagecolorexactalpha imagecolormatch imagecolorresolve imagecolorresolvealpha imagecolorset imagecolorsforindex imagecolorstotal imagecolortransparent imagecopy imagecopymerge imagecopymergegray imagecopyresampled imagecopyresized imagecreate imagecreatefromgd2 imagecreatefromgd2part imagecreatefromgd imagecreatefromgif imagecreatefromjpeg imagecreatefrompng imagecreatefromstring imagecreatefromwbmp imagecreatefromxbm imagecreatefromxpm imagecreatetruecolor imagedashedline imagedestroy imageellipse imagefill imagefilledarc imagefilledellipse imagefilledpolygon imagefilledrectangle imagefilltoborder imagefontheight imagefontwidth imageftbbox imagefttext imagegammacorrect imagegd2 imagegd imagegif imageinterlace imageistruecolor imagejpeg imageline imageloadfont imagepalettecopy imagepng imagepolygon imagepsbbox imagepscopyfont imagepsencodefont imagepsextendfont imagepsfreefont imagepsloadfont imagepsslantfont imagepstext imagerectangle imagerotate imagesavealpha imagesetbrush imagesetpixel imagesetstyle imagesetthickness imagesettile imagestring imagestringup imagesx imagesy imagetruecolortopalette imagettfbbox imagettftext imagetypes imagewbmp iptcembed iptcparse jpeg2wbmp png2wbmp read_exif_data contained +syn keyword phpFunctions igbinary_serialize igbinary_unserialize contained +syn keyword phpFunctions exif_imagetype exif_read_data exif_thumbnail gd_info getimagesize image_type_to_mime_type image2wbmp imagealphablending imageantialias imagearc imagechar imagecharup imagecolorallocate imagecolorallocatealpha imagecolorat imagecolorclosest imagecolorclosestalpha imagecolorclosesthwb imagecolordeallocate imagecolorexact imagecolorexactalpha imagecolormatch imagecolorresolve imagecolorresolvealpha imagecolorset imagecolorsforindex imagecolorstotal imagecolortransparent imagecopy imagecopymerge imagecopymergegray imagecopyresampled imagecopyresized imagecreate imagecreatefromgd2 imagecreatefromgd2part imagecreatefromgd imagecreatefromgif imagecreatefromjpeg imagecreatefrompng imagecreatefromstring imagecreatefromwbmp imagecreatefromxbm imagecreatefromxpm imagecreatetruecolor imagedashedline imagedestroy imageellipse imagefill imagefilledarc imagefilledellipse imagefilledpolygon imagefilledrectangle imagefilltoborder imagefontheight imagefontwidth imageftbbox imagefttext imagegammacorrect imagegd2 imagegd imagegif imageinterlace imageistruecolor imagejpeg imageline imageloadfont imagepalettecopy imagepng imagepolygon imagepsbbox imagepscopyfont imagepsencodefont imagepsextendfont imagepsfreefont imagepsloadfont imagepsslantfont imagepstext imagerectangle imagerotate imagesavealpha imagesetbrush imagesetpixel imagesetstyle imagesetthickness imagesettile imagestring imagestringup imagesx imagesy imagetruecolortopalette imagettfbbox imagettftext imagetypes imagewbmp iptcembed iptcparse jpeg2wbmp png2wbmp read_exif_data exif_tagname imageaffine imageaffinematrixconcat imageaffinematrixget imagebmp imageconvolution imagecreatefrombmp imagecreatefromtga imagecrop imagecropauto imagefilter imageflip imagegetclip imagelayereffect imageopenpolygon imagepalettetotruecolor imageresolution imagescale imagesetclip imagesetinterpolation imagexbm contained syn keyword phpFunctions imap_8bit imap_alerts imap_append imap_base64 imap_binary imap_body imap_bodystruct imap_check imap_clearflag_full imap_close imap_createmailbox imap_delete imap_deletemailbox imap_errors imap_expunge imap_fetch_overview imap_fetchbody imap_fetchheader imap_fetchstructure imap_get_quota imap_get_quotaroot imap_getacl imap_getmailboxes imap_getsubscribed imap_header imap_headerinfo imap_headers imap_last_error imap_list imap_listmailbox imap_listscan imap_listsubscribed imap_lsub imap_mail_compose imap_mail_copy imap_mail_move imap_mail imap_mailboxmsginfo imap_mime_header_decode imap_msgno imap_num_msg imap_num_recent imap_open imap_ping imap_qprint imap_renamemailbox imap_reopen imap_rfc822_parse_adrlist imap_rfc822_parse_headers imap_rfc822_write_address imap_scanmailbox imap_search imap_set_quota imap_setacl imap_setflag_full imap_sort imap_status imap_subscribe imap_thread imap_timeout imap_uid imap_undelete imap_unsubscribe imap_utf7_decode imap_utf7_encode imap_utf8 contained syn keyword phpFunctions assert_options assert dl extension_loaded get_cfg_var get_current_user get_defined_constants get_extension_funcs get_include_path get_included_files get_loaded_extensions get_magic_quotes_gpc get_magic_quotes_runtime get_required_files getenv getlastmod getmygid getmyinode getmypid getmyuid getopt getrusage ini_alter ini_get_all ini_get ini_restore ini_set main memory_get_usage php_ini_scanned_files php_logo_guid php_sapi_name php_uname phpcredits phpinfo phpversion putenv restore_include_path set_include_path set_magic_quotes_runtime set_time_limit version_compare zend_logo_guid zend_version contained syn keyword phpFunctions ingres_autocommit ingres_close ingres_commit ingres_connect ingres_fetch_array ingres_fetch_object ingres_fetch_row ingres_field_length ingres_field_name ingres_field_nullable ingres_field_precision ingres_field_scale ingres_field_type ingres_num_fields ingres_num_rows ingres_pconnect ingres_query ingres_rollback contained +syn keyword phpFunctions collator_asort collator_compare collator_create collator_get_attribute collator_get_error_code collator_get_error_message collator_get_locale collator_get_sort_key collator_get_strength collator_set_attribute collator_set_strength collator_sort collator_sort_with_sort_keys datefmt_create datefmt_format datefmt_format_object datefmt_get_calendar datefmt_get_calendar_object datefmt_get_datetype datefmt_get_error_code datefmt_get_error_message datefmt_get_locale datefmt_get_pattern datefmt_get_timetype datefmt_get_timezone datefmt_get_timezone_id datefmt_is_lenient datefmt_localtime datefmt_parse datefmt_set_calendar datefmt_set_lenient datefmt_set_pattern datefmt_set_timezone grapheme_extract grapheme_stripos grapheme_stristr grapheme_strlen grapheme_strpos grapheme_strripos grapheme_strrpos grapheme_strstr grapheme_substr idn_to_ascii idn_to_utf8 intl_error_name intl_get_error_code intl_get_error_message intl_is_failure intlcal_add intlcal_after intlcal_before intlcal_clear intlcal_create_instance intlcal_equals intlcal_field_difference intlcal_from_date_time intlcal_get intlcal_get_actual_maximum intlcal_get_actual_minimum intlcal_get_available_locales intlcal_get_day_of_week_type intlcal_get_error_code intlcal_get_error_message intlcal_get_first_day_of_week intlcal_get_greatest_minimum intlcal_get_keyword_values_for_locale intlcal_get_least_maximum intlcal_get_locale intlcal_get_maximum intlcal_get_minimal_days_in_first_week intlcal_get_minimum intlcal_get_now intlcal_get_repeated_wall_time_option intlcal_get_skipped_wall_time_option intlcal_get_time intlcal_get_time_zone intlcal_get_type intlcal_get_weekend_transition intlcal_in_daylight_time intlcal_is_equivalent_to intlcal_is_lenient intlcal_is_set intlcal_is_weekend intlcal_roll intlcal_set intlcal_set_first_day_of_week intlcal_set_lenient intlcal_set_minimal_days_in_first_week intlcal_set_repeated_wall_time_option intlcal_set_skipped_wall_time_option intlcal_set_time intlcal_set_time_zone intlcal_to_date_time intlgregcal_create_instance intlgregcal_get_gregorian_change intlgregcal_is_leap_year intlgregcal_set_gregorian_change intltz_count_equivalent_ids intltz_create_default intltz_create_enumeration intltz_create_time_zone intltz_create_time_zone_id_enumeration intltz_from_date_time_zone intltz_get_canonical_id intltz_get_display_name intltz_get_dst_savings intltz_get_equivalent_id intltz_get_error_code intltz_get_error_message intltz_get_gmt intltz_get_id intltz_get_offset intltz_get_raw_offset intltz_get_region intltz_get_tz_data_version intltz_get_unknown intltz_has_same_rules intltz_to_date_time_zone intltz_use_daylight_time locale_accept_from_http locale_canonicalize locale_compose locale_filter_matches locale_get_all_variants locale_get_default locale_get_display_language locale_get_display_name locale_get_display_region locale_get_display_script locale_get_display_variant locale_get_keywords locale_get_primary_language locale_get_region locale_get_script locale_lookup locale_parse locale_set_default msgfmt_create msgfmt_format msgfmt_format_message msgfmt_get_error_code msgfmt_get_error_message msgfmt_get_locale msgfmt_get_pattern msgfmt_parse msgfmt_parse_message msgfmt_set_pattern normalizer_is_normalized normalizer_normalize numfmt_create numfmt_format numfmt_format_currency numfmt_get_attribute numfmt_get_error_code numfmt_get_error_message numfmt_get_locale numfmt_get_pattern numfmt_get_symbol numfmt_get_text_attribute numfmt_parse numfmt_parse_currency numfmt_set_attribute numfmt_set_pattern numfmt_set_symbol numfmt_set_text_attribute resourcebundle_count resourcebundle_create resourcebundle_get resourcebundle_get_error_code resourcebundle_get_error_message resourcebundle_locales transliterator_create transliterator_create_from_rules transliterator_create_inverse transliterator_get_error_code transliterator_get_error_message transliterator_list_ids transliterator_transliterate contained syn keyword phpFunctions ircg_channel_mode ircg_disconnect ircg_fetch_error_msg ircg_get_username ircg_html_encode ircg_ignore_add ircg_ignore_del ircg_is_conn_alive ircg_join ircg_kick ircg_lookup_format_messages ircg_msg ircg_nick ircg_nickname_escape ircg_nickname_unescape ircg_notice ircg_part ircg_pconnect ircg_register_format_messages ircg_set_current ircg_set_file ircg_set_on_die ircg_topic ircg_whois contained syn keyword phpFunctions java_last_exception_clear java_last_exception_get contained -syn keyword phpFunctions json_decode json_encode json_last_error contained +syn keyword phpFunctions json_decode json_encode json_last_error json_last_error_msg contained syn keyword phpFunctions ldap_8859_to_t61 ldap_add ldap_bind ldap_close ldap_compare ldap_connect ldap_count_entries ldap_delete ldap_dn2ufn ldap_err2str ldap_errno ldap_error ldap_explode_dn ldap_first_attribute ldap_first_entry ldap_first_reference ldap_free_result ldap_get_attributes ldap_get_dn ldap_get_entries ldap_get_option ldap_get_values_len ldap_get_values ldap_list ldap_mod_add ldap_mod_del ldap_mod_replace ldap_modify ldap_next_attribute ldap_next_entry ldap_next_reference ldap_parse_reference ldap_parse_result ldap_read ldap_rename ldap_search ldap_set_option ldap_set_rebind_proc ldap_sort ldap_start_tls ldap_t61_to_8859 ldap_unbind contained +syn keyword phpFunctions libxml_clear_errors libxml_disable_entity_loader libxml_get_errors libxml_get_last_error libxml_set_external_entity_loader libxml_set_streams_context libxml_use_internal_errors contained syn keyword phpFunctions lzf_compress lzf_decompress lzf_optimized_for contained syn keyword phpFunctions ezmlm_hash mail contained syn keyword phpFunctions mailparse_determine_best_xfer_encoding mailparse_msg_create mailparse_msg_extract_part_file mailparse_msg_extract_part mailparse_msg_free mailparse_msg_get_part_data mailparse_msg_get_part mailparse_msg_get_structure mailparse_msg_parse_file mailparse_msg_parse mailparse_rfc822_parse_addresses mailparse_stream_encode mailparse_uudecode_all contained syn keyword phpFunctions abs acos acosh asin asinh atan2 atan atanh base_convert bindec ceil cos cosh decbin dechex decoct deg2rad exp expm1 floor fmod getrandmax hexdec hypot is_finite is_infinite is_nan lcg_value log10 log1p log max min mt_getrandmax mt_rand mt_srand octdec pi pow rad2deg rand round sin sinh sqrt srand tan tanh contained -syn keyword phpFunctions mb_convert_case mb_convert_encoding mb_convert_kana mb_convert_variables mb_decode_mimeheader mb_decode_numericentity mb_detect_encoding mb_detect_order mb_encode_mimeheader mb_encode_numericentity mb_ereg_match mb_ereg_replace mb_ereg_search_getpos mb_ereg_search_getregs mb_ereg_search_init mb_ereg_search_pos mb_ereg_search_regs mb_ereg_search_setpos mb_ereg_search mb_ereg mb_eregi_replace mb_eregi mb_get_info mb_http_input mb_http_output mb_internal_encoding mb_language mb_output_handler mb_parse_str mb_preferred_mime_name mb_regex_encoding mb_regex_set_options mb_send_mail mb_split mb_strcut mb_strimwidth mb_strlen mb_strpos mb_strrpos mb_strtolower mb_strtoupper mb_strwidth mb_substitute_character mb_substr_count mb_substr contained +syn keyword phpFunctions array_key_first array_key_last boolval cli_get_process_title cli_set_process_title convert_uudecode convert_uuencode debug_zval_dump error_clear_last error_get_last forward_static_call forward_static_call_array fputcsv get_headers gethostname getimagesizefromstring header_register_callback header_remove hex2bin hrtime htmlspecialchars_decode http_response_code image_type_to_extension inet_ntop inet_pton intdiv is_countable is_iterable lcfirst lchgrp lchown memory_get_peak_usage net_get_interfaces parse_ini_string password_algos password_get_info password_hash password_needs_rehash password_verify php_ini_loaded_file php_strip_whitespace quoted_printable_encode random_bytes random_int realpath_cache_get realpath_cache_size setrawcookie str_getcsv stream_bucket_append stream_bucket_make_writeable stream_bucket_new stream_bucket_prepend stream_context_get_default stream_context_get_params stream_context_set_default stream_filter_remove stream_is_local stream_isatty stream_resolve_include_path stream_set_chunk_size stream_set_read_buffer stream_socket_enable_crypto stream_socket_pair stream_socket_shutdown stream_supports_lock stream_wrapper_restore stream_wrapper_unregister strpbrk strptime sys_get_temp_dir sys_getloadavg time_nanosleep time_sleep_until vfprintf contained + +syn keyword phpFunctions mb_convert_case mb_convert_encoding mb_convert_kana mb_convert_variables mb_decode_mimeheader mb_decode_numericentity mb_detect_encoding mb_detect_order mb_encode_mimeheader mb_encode_numericentity mb_ereg_match mb_ereg_replace mb_ereg_search_getpos mb_ereg_search_getregs mb_ereg_search_init mb_ereg_search_pos mb_ereg_search_regs mb_ereg_search_setpos mb_ereg_search mb_ereg mb_eregi_replace mb_eregi mb_get_info mb_http_input mb_http_output mb_internal_encoding mb_language mb_output_handler mb_parse_str mb_preferred_mime_name mb_regex_encoding mb_regex_set_options mb_send_mail mb_split mb_strcut mb_strimwidth mb_strlen mb_strpos mb_strrpos mb_strtolower mb_strtoupper mb_strwidth mb_substitute_character mb_substr_count mb_substr mb_check_encoding mb_chr mb_encoding_aliases mb_ereg_replace_callback mb_list_encodings mb_ord mb_scrub mb_str_split mb_stripos mb_stristr mb_strrchr mb_strrichr mb_strripos mb_strstr mbereg mbereg_match mbereg_replace mbereg_search mbereg_search_getpos mbereg_search_getregs mbereg_search_init mbereg_search_pos mbereg_search_regs mbereg_search_setpos mberegi mberegi_replace mbregex_encoding mbsplit contained syn keyword phpFunctions mcal_append_event mcal_close mcal_create_calendar mcal_date_compare mcal_date_valid mcal_day_of_week mcal_day_of_year mcal_days_in_month mcal_delete_calendar mcal_delete_event mcal_event_add_attribute mcal_event_init mcal_event_set_alarm mcal_event_set_category mcal_event_set_class mcal_event_set_description mcal_event_set_end mcal_event_set_recur_daily mcal_event_set_recur_monthly_mday mcal_event_set_recur_monthly_wday mcal_event_set_recur_none mcal_event_set_recur_weekly mcal_event_set_recur_yearly mcal_event_set_start mcal_event_set_title mcal_expunge mcal_fetch_current_stream_event mcal_fetch_event mcal_is_leap_year mcal_list_alarms mcal_list_events mcal_next_recurrence mcal_open mcal_popen mcal_rename_calendar mcal_reopen mcal_snooze mcal_store_event mcal_time_valid mcal_week_of_year contained syn keyword phpFunctions mcrypt_cbc mcrypt_cfb mcrypt_create_iv mcrypt_decrypt mcrypt_ecb mcrypt_enc_get_algorithms_name mcrypt_enc_get_block_size mcrypt_enc_get_iv_size mcrypt_enc_get_key_size mcrypt_enc_get_modes_name mcrypt_enc_get_supported_key_sizes mcrypt_enc_is_block_algorithm_mode mcrypt_enc_is_block_algorithm mcrypt_enc_is_block_mode mcrypt_enc_self_test mcrypt_encrypt mcrypt_generic_deinit mcrypt_generic_end mcrypt_generic_init mcrypt_generic mcrypt_get_block_size mcrypt_get_cipher_name mcrypt_get_iv_size mcrypt_get_key_size mcrypt_list_algorithms mcrypt_list_modes mcrypt_module_close mcrypt_module_get_algo_block_size mcrypt_module_get_algo_key_size mcrypt_module_get_supported_key_sizes mcrypt_module_is_block_algorithm_mode mcrypt_module_is_block_algorithm mcrypt_module_is_block_mode mcrypt_module_open mcrypt_module_self_test mcrypt_ofb mdecrypt_generic contained syn keyword phpFunctions mcve_adduser mcve_adduserarg mcve_bt mcve_checkstatus mcve_chkpwd mcve_chngpwd mcve_completeauthorizations mcve_connect mcve_connectionerror mcve_deleteresponse mcve_deletetrans mcve_deleteusersetup mcve_deluser mcve_destroyconn mcve_destroyengine mcve_disableuser mcve_edituser mcve_enableuser mcve_force mcve_getcell mcve_getcellbynum mcve_getcommadelimited mcve_getheader mcve_getuserarg mcve_getuserparam mcve_gft mcve_gl mcve_gut mcve_initconn mcve_initengine mcve_initusersetup mcve_iscommadelimited mcve_liststats mcve_listusers mcve_maxconntimeout mcve_monitor mcve_numcolumns mcve_numrows mcve_override mcve_parsecommadelimited mcve_ping mcve_preauth mcve_preauthcompletion mcve_qc mcve_responseparam mcve_return mcve_returncode mcve_returnstatus mcve_sale mcve_setblocking mcve_setdropfile mcve_setip mcve_setssl_files mcve_setssl mcve_settimeout mcve_settle mcve_text_avs mcve_text_code mcve_text_cv mcve_transactionauth mcve_transactionavs mcve_transactionbatch mcve_transactioncv mcve_transactionid mcve_transactionitem mcve_transactionssent mcve_transactiontext mcve_transinqueue mcve_transnew mcve_transparam mcve_transsend mcve_ub mcve_uwait mcve_verifyconnection mcve_verifysslcert mcve_void contained syn keyword phpFunctions mhash_count mhash_get_block_size mhash_get_hash_name mhash_keygen_s2k mhash contained -syn keyword phpFunctions mime_content_type contained +syn keyword phpFunctions mime_content_type finfo_buffer finfo_close finfo_file finfo_open finfo_set_flags contained syn keyword phpFunctions ming_setcubicthreshold ming_setscale ming_useswfversion SWFAction SWFBitmap swfbutton_keypress SWFbutton SWFDisplayItem SWFFill SWFFont SWFGradient SWFMorph SWFMovie SWFShape SWFSprite SWFText SWFTextField contained syn keyword phpMethods getHeight getWidth addAction addShape setAction setdown setHit setOver setUp addColor move moveTo multColor remove Rotate rotateTo scale scaleTo setDepth setName setRatio skewX skewXTo skewY skewYTo moveTo rotateTo scaleTo skewXTo skewYTo getwidth addEntry getshape1 getshape2 add nextframe output remove save setbackground setdimension setframes setrate streammp3 addFill drawCurve drawCurveTo drawLine drawLineTo movePen movePenTo setLeftFill setLine setRightFill add nextframe remove setframes addString getWidth moveTo setColor setFont setHeight setSpacing addstring align setbounds setcolor setFont setHeight setindentation setLeftMargin setLineSpacing setMargins setname setrightMargin contained syn keyword phpFunctions connection_aborted connection_status connection_timeout constant define defined die eval exit get_browser highlight_file highlight_string ignore_user_abort pack show_source sleep uniqid unpack usleep contained @@ -188,7 +202,7 @@ syn keyword phpFunctions msql_affected_rows msql_close msql_connect msql_create_ syn keyword phpFunctions mssql_bind mssql_close mssql_connect mssql_data_seek mssql_execute mssql_fetch_array mssql_fetch_assoc mssql_fetch_batch mssql_fetch_field mssql_fetch_object mssql_fetch_row mssql_field_length mssql_field_name mssql_field_seek mssql_field_type mssql_free_result mssql_free_statement mssql_get_last_message mssql_guid_string mssql_init mssql_min_error_severity mssql_min_message_severity mssql_next_result mssql_num_fields mssql_num_rows mssql_pconnect mssql_query mssql_result mssql_rows_affected mssql_select_db contained syn keyword phpFunctions muscat_close muscat_get muscat_give muscat_setup_net muscat_setup contained syn keyword phpFunctions mysql_affected_rows mysql_change_user mysql_client_encoding mysql_close mysql_connect mysql_create_db mysql_data_seek mysql_db_name mysql_db_query mysql_drop_db mysql_errno mysql_error mysql_escape_string mysql_fetch_array mysql_fetch_assoc mysql_fetch_field mysql_fetch_lengths mysql_fetch_object mysql_fetch_row mysql_field_flags mysql_field_len mysql_field_name mysql_field_seek mysql_field_table mysql_field_type mysql_free_result mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql_insert_id mysql_list_dbs mysql_list_fields mysql_list_processes mysql_list_tables mysql_num_fields mysql_num_rows mysql_pconnect mysql_ping mysql_query mysql_real_escape_string mysql_result mysql_select_db mysql_stat mysql_tablename mysql_thread_id mysql_unbuffered_query contained -syn keyword phpFunctions mysqli_affected_rows mysqli_autocommit mysqli_bind_param mysqli_bind_result mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect mysqli_data_seek mysqli_debug mysqli_disable_reads_from_master mysqli_disable_rpl_parse mysqli_dump_debug_info mysqli_enable_reads_from_master mysqli_enable_rpl_parse mysqli_errno mysqli_error mysqli_execute mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_fetch mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_client_info mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_master_query mysqli_num_fields mysqli_num_rows mysqli_options mysqli_param_count mysqli_ping mysqli_prepare_result mysqli_prepare mysqli_profiler mysqli_query mysqli_read_query_result mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reload mysqli_rollback mysqli_rpl_parse_enabled mysqli_rpl_probe mysqli_rpl_query_type mysqli_select_db mysqli_send_long_data mysqli_send_query mysqli_slave_query mysqli_ssl_set mysqli_stat mysqli_stmt_affected_rows mysqli_stmt_close mysqli_stmt_errno mysqli_stmt_error mysqli_stmt_store_result mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count contained +syn keyword phpFunctions mysqli_affected_rows mysqli_autocommit mysqli_bind_param mysqli_bind_result mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect mysqli_data_seek mysqli_debug mysqli_disable_reads_from_master mysqli_disable_rpl_parse mysqli_dump_debug_info mysqli_enable_reads_from_master mysqli_enable_rpl_parse mysqli_errno mysqli_error mysqli_execute mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_fetch mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_client_info mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_master_query mysqli_num_fields mysqli_num_rows mysqli_options mysqli_param_count mysqli_ping mysqli_prepare_result mysqli_prepare mysqli_profiler mysqli_query mysqli_read_query_result mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reload mysqli_rollback mysqli_rpl_parse_enabled mysqli_rpl_probe mysqli_rpl_query_type mysqli_select_db mysqli_send_long_data mysqli_send_query mysqli_slave_query mysqli_ssl_set mysqli_stat mysqli_stmt_affected_rows mysqli_stmt_close mysqli_stmt_errno mysqli_stmt_error mysqli_stmt_store_result mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count mysqli_begin_transaction mysqli_connect_errno mysqli_connect_error mysqli_error_list mysqli_escape_string mysqli_fetch_all mysqli_get_charset mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_links_stats mysqli_get_warnings mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_poll mysqli_reap_async_query mysqli_refresh mysqli_release_savepoint mysqli_report mysqli_savepoint mysqli_set_charset mysqli_set_opt mysqli_sqlstate mysqli_stmt_attr_get mysqli_stmt_attr_set mysqli_stmt_bind_param mysqli_stmt_bind_result mysqli_stmt_data_seek mysqli_stmt_error_list mysqli_stmt_execute mysqli_stmt_fetch mysqli_stmt_field_count mysqli_stmt_free_result mysqli_stmt_get_result mysqli_stmt_get_warnings mysqli_stmt_init mysqli_stmt_insert_id mysqli_stmt_more_results mysqli_stmt_next_result mysqli_stmt_num_rows mysqli_stmt_param_count mysqli_stmt_prepare mysqli_stmt_reset mysqli_stmt_result_metadata mysqli_stmt_send_long_data mysqli_stmt_sqlstate contained syn keyword phpFunctions ncurses_addch ncurses_addchnstr ncurses_addchstr ncurses_addnstr ncurses_addstr ncurses_assume_default_colors ncurses_attroff ncurses_attron ncurses_attrset ncurses_baudrate ncurses_beep ncurses_bkgd ncurses_bkgdset ncurses_border ncurses_bottom_panel ncurses_can_change_color ncurses_cbreak ncurses_clear ncurses_clrtobot ncurses_clrtoeol ncurses_color_content ncurses_color_set ncurses_curs_set ncurses_def_prog_mode ncurses_def_shell_mode ncurses_define_key ncurses_del_panel ncurses_delay_output ncurses_delch ncurses_deleteln ncurses_delwin ncurses_doupdate ncurses_echo ncurses_echochar ncurses_end ncurses_erase ncurses_erasechar ncurses_filter ncurses_flash ncurses_flushinp ncurses_getch ncurses_getmaxyx ncurses_getmouse ncurses_getyx ncurses_halfdelay ncurses_has_colors ncurses_has_ic ncurses_has_il ncurses_has_key ncurses_hide_panel ncurses_hline ncurses_inch ncurses_init_color ncurses_init_pair ncurses_init ncurses_insch ncurses_insdelln ncurses_insertln ncurses_insstr ncurses_instr ncurses_isendwin ncurses_keyok ncurses_keypad ncurses_killchar ncurses_longname ncurses_meta ncurses_mouse_trafo ncurses_mouseinterval ncurses_mousemask ncurses_move_panel ncurses_move ncurses_mvaddch ncurses_mvaddchnstr ncurses_mvaddchstr ncurses_mvaddnstr ncurses_mvaddstr ncurses_mvcur ncurses_mvdelch ncurses_mvgetch ncurses_mvhline ncurses_mvinch ncurses_mvvline ncurses_mvwaddstr ncurses_napms ncurses_new_panel ncurses_newpad ncurses_newwin ncurses_nl ncurses_nocbreak ncurses_noecho ncurses_nonl ncurses_noqiflush ncurses_noraw ncurses_pair_content ncurses_panel_above ncurses_panel_below ncurses_panel_window ncurses_pnoutrefresh ncurses_prefresh ncurses_putp ncurses_qiflush ncurses_raw ncurses_refresh ncurses_replace_panel ncurses_reset_prog_mode ncurses_reset_shell_mode ncurses_resetty ncurses_savetty ncurses_scr_dump ncurses_scr_init ncurses_scr_restore ncurses_scr_set ncurses_scrl ncurses_show_panel ncurses_slk_attr ncurses_slk_attroff ncurses_slk_attron ncurses_slk_attrset ncurses_slk_clear ncurses_slk_color ncurses_slk_init ncurses_slk_noutrefresh ncurses_slk_refresh ncurses_slk_restore ncurses_slk_set ncurses_slk_touch ncurses_standend ncurses_standout ncurses_start_color ncurses_termattrs ncurses_termname ncurses_timeout ncurses_top_panel ncurses_typeahead ncurses_ungetch ncurses_ungetmouse ncurses_update_panels ncurses_use_default_colors ncurses_use_env ncurses_use_extended_names ncurses_vidattr ncurses_vline ncurses_waddch ncurses_waddstr ncurses_wattroff ncurses_wattron ncurses_wattrset ncurses_wborder ncurses_wclear ncurses_wcolor_set ncurses_werase ncurses_wgetch ncurses_whline ncurses_wmouse_trafo ncurses_wmove ncurses_wnoutrefresh ncurses_wrefresh ncurses_wstandend ncurses_wstandout ncurses_wvline contained syn keyword phpFunctions checkdnsrr closelog debugger_off debugger_on define_syslog_variables dns_check_record dns_get_mx dns_get_record fsockopen gethostbyaddr gethostbyname gethostbynamel getmxrr getprotobyname getprotobynumber getservbyname getservbyport ip2long long2ip openlog pfsockopen socket_get_status socket_set_blocking socket_set_timeout syslog contained syn keyword phpFunctions yp_all yp_cat yp_err_string yp_errno yp_first yp_get_default_domain yp_master yp_match yp_next yp_order contained @@ -197,48 +211,55 @@ syn keyword phpFunctions nsapi_request_headers nsapi_response_headers nsapi_virt syn keyword phpFunctions aggregate_info aggregate_methods_by_list aggregate_methods_by_regexp aggregate_methods aggregate_properties_by_list aggregate_properties_by_regexp aggregate_properties aggregate aggregation_info deaggregate contained syn keyword phpFunctions ocibindbyname ocicancel ocicloselob ocicollappend ocicollassign ocicollassignelem ocicollgetelem ocicollmax ocicollsize ocicolltrim ocicolumnisnull ocicolumnname ocicolumnprecision ocicolumnscale ocicolumnsize ocicolumntype ocicolumntyperaw ocicommit ocidefinebyname ocierror ociexecute ocifetch ocifetchinto ocifetchstatement ocifreecollection ocifreecursor ocifreedesc ocifreestatement ociinternaldebug ociloadlob ocilogoff ocilogon ocinewcollection ocinewcursor ocinewdescriptor ocinlogon ocinumcols ociparse ociplogon ociresult ocirollback ocirowcount ocisavelob ocisavelobfile ociserverversion ocisetprefetch ocistatementtype ociwritelobtofile ociwritetemporarylob contained syn keyword phpFunctions odbc_autocommit odbc_binmode odbc_close_all odbc_close odbc_columnprivileges odbc_columns odbc_commit odbc_connect odbc_cursor odbc_data_source odbc_do odbc_error odbc_errormsg odbc_exec odbc_execute odbc_fetch_array odbc_fetch_into odbc_fetch_object odbc_fetch_row odbc_field_len odbc_field_name odbc_field_num odbc_field_precision odbc_field_scale odbc_field_type odbc_foreignkeys odbc_free_result odbc_gettypeinfo odbc_longreadlen odbc_next_result odbc_num_fields odbc_num_rows odbc_pconnect odbc_prepare odbc_primarykeys odbc_procedurecolumns odbc_procedures odbc_result_all odbc_result odbc_rollback odbc_setoption odbc_specialcolumns odbc_statistics odbc_tableprivileges odbc_tables contained -syn keyword phpFunctions openssl_cipher_iv_length openssl_csr_export_to_file openssl_csr_export openssl_csr_get_public_key openssl_csr_get_subject openssl_csr_new openssl_csr_sign openssl_decrypt openssl_dh_compute_key openssl_digest openssl_encrypt openssl_error_string openssl_free_key openssl_get_cert_locations openssl_get_cipher_methods openssl_get_md_methods openssl_get_privatekey openssl_get_publickey openssl_open openssl_pbkdf2 openssl_pkcs12_export_to_file openssl_pkcs12_export openssl_pkcs12_read openssl_pkcs7_decrypt openssl_pkcs7_encrypt openssl_pkcs7_sign openssl_pkcs7_verify openssl_pkey_export_to_file openssl_pkey_export openssl_pkey_free openssl_pkey_get_details openssl_pkey_get_private openssl_pkey_get_public openssl_pkey_new openssl_private_decrypt openssl_private_encrypt openssl_public_decrypt openssl_public_encrypt openssl_random_pseudo_bytes openssl_seal openssl_sign openssl_spki_export_challenge openssl_spki_export openssl_spki_new openssl_spki_verify openssl_verify openssl_x509_check_private_key openssl_x509_checkpurpose openssl_x509_export_to_file openssl_x509_export openssl_x509_fingerprint openssl_x509_free openssl_x509_parse openssl_x509_read contained +syn keyword phpFunctions opcache_reset opcache_invalidate opcache_compile_file opcache_is_script_cached opcache_get_configuration opcache_get_status contained +syn keyword phpFunctions openssl_cipher_iv_length openssl_csr_export_to_file openssl_csr_export openssl_csr_get_public_key openssl_csr_get_subject openssl_csr_new openssl_csr_sign openssl_decrypt openssl_dh_compute_key openssl_digest openssl_encrypt openssl_error_string openssl_free_key openssl_get_cert_locations openssl_get_cipher_methods openssl_get_md_methods openssl_get_privatekey openssl_get_publickey openssl_open openssl_pbkdf2 openssl_pkcs12_export_to_file openssl_pkcs12_export openssl_pkcs12_read openssl_pkcs7_decrypt openssl_pkcs7_encrypt openssl_pkcs7_sign openssl_pkcs7_verify openssl_pkey_export_to_file openssl_pkey_export openssl_pkey_free openssl_pkey_get_details openssl_pkey_get_private openssl_pkey_get_public openssl_pkey_new openssl_private_decrypt openssl_private_encrypt openssl_public_decrypt openssl_public_encrypt openssl_random_pseudo_bytes openssl_seal openssl_sign openssl_spki_export_challenge openssl_spki_export openssl_spki_new openssl_spki_verify openssl_verify openssl_x509_check_private_key openssl_x509_checkpurpose openssl_x509_export_to_file openssl_x509_export openssl_x509_fingerprint openssl_x509_free openssl_x509_parse openssl_x509_read openssl_get_curve_names openssl_pkcs7_read openssl_pkey_derive openssl_x509_verify contained syn keyword phpFunctions ora_bind ora_close ora_columnname ora_columnsize ora_columntype ora_commit ora_commitoff ora_commiton ora_do ora_error ora_errorcode ora_exec ora_fetch_into ora_fetch ora_getcolumn ora_logoff ora_logon ora_numcols ora_numrows ora_open ora_parse ora_plogon ora_rollback contained syn keyword phpFunctions flush ob_clean ob_end_clean ob_end_flush ob_flush ob_get_clean ob_get_contents ob_get_flush ob_get_length ob_get_level ob_get_status ob_gzhandler ob_implicit_flush ob_list_handlers ob_start output_add_rewrite_var output_reset_rewrite_vars contained syn keyword phpFunctions overload contained syn keyword phpFunctions ovrimos_close ovrimos_commit ovrimos_connect ovrimos_cursor ovrimos_exec ovrimos_execute ovrimos_fetch_into ovrimos_fetch_row ovrimos_field_len ovrimos_field_name ovrimos_field_num ovrimos_field_type ovrimos_free_result ovrimos_longreadlen ovrimos_num_fields ovrimos_num_rows ovrimos_prepare ovrimos_result_all ovrimos_result ovrimos_rollback contained -syn keyword phpFunctions pcntl_exec pcntl_fork pcntl_signal pcntl_waitpid pcntl_wexitstatus pcntl_wifexited pcntl_wifsignaled pcntl_wifstopped pcntl_wstopsig pcntl_wtermsig contained -syn keyword phpFunctions preg_grep preg_match_all preg_match preg_quote preg_replace_callback preg_replace preg_split contained +syn keyword phpFunctions pcntl_exec pcntl_fork pcntl_signal pcntl_waitpid pcntl_wexitstatus pcntl_wifexited pcntl_wifsignaled pcntl_wifstopped pcntl_wstopsig pcntl_wtermsig pcntl_alarm pcntl_async_signals pcntl_errno pcntl_get_last_error pcntl_getpriority pcntl_setpriority pcntl_signal_dispatch pcntl_signal_get_handler pcntl_sigprocmask pcntl_sigtimedwait pcntl_sigwaitinfo pcntl_strerror pcntl_unshare pcntl_wait pcntl_wifcontinued contained +syn keyword phpFunctions preg_filter preg_grep preg_last_error preg_match_all preg_match preg_quote preg_replace_callback preg_replace_callback_array preg_replace preg_split contained +syn keyword phpFunctions pdo_drivers contained syn keyword phpFunctions pdf_add_annotation pdf_add_bookmark pdf_add_launchlink pdf_add_locallink pdf_add_note pdf_add_outline pdf_add_pdflink pdf_add_thumbnail pdf_add_weblink pdf_arc pdf_arcn pdf_attach_file pdf_begin_page pdf_begin_pattern pdf_begin_template pdf_circle pdf_clip pdf_close_image pdf_close_pdi_page pdf_close_pdi pdf_close pdf_closepath_fill_stroke pdf_closepath_stroke pdf_closepath pdf_concat pdf_continue_text pdf_curveto pdf_delete pdf_end_page pdf_end_pattern pdf_end_template pdf_endpath pdf_fill_stroke pdf_fill pdf_findfont pdf_get_buffer pdf_get_font pdf_get_fontname pdf_get_fontsize pdf_get_image_height pdf_get_image_width pdf_get_majorversion pdf_get_minorversion pdf_get_parameter pdf_get_pdi_parameter pdf_get_pdi_value pdf_get_value pdf_initgraphics pdf_lineto pdf_makespotcolor pdf_moveto pdf_new pdf_open_CCITT pdf_open_file pdf_open_gif pdf_open_image_file pdf_open_image pdf_open_jpeg pdf_open_memory_image pdf_open_pdi_page pdf_open_pdi pdf_open_png pdf_open_tiff pdf_open pdf_place_image pdf_place_pdi_page pdf_rect pdf_restore pdf_rotate pdf_save pdf_scale pdf_set_border_color pdf_set_border_dash pdf_set_border_style pdf_set_char_spacing pdf_set_duration pdf_set_font pdf_set_horiz_scaling pdf_set_info_author pdf_set_info_creator pdf_set_info_keywords pdf_set_info_subject pdf_set_info_title pdf_set_info pdf_set_leading pdf_set_parameter pdf_set_text_matrix pdf_set_text_pos pdf_set_text_rendering pdf_set_text_rise pdf_set_value pdf_set_word_spacing pdf_setcolor pdf_setdash pdf_setflat pdf_setfont pdf_setgray_fill pdf_setgray_stroke pdf_setgray pdf_setlinecap pdf_setlinejoin pdf_setlinewidth pdf_setmatrix pdf_setmiterlimit pdf_setpolydash pdf_setrgbcolor_fill pdf_setrgbcolor_stroke pdf_setrgbcolor pdf_show_boxed pdf_show_xy pdf_show pdf_skew pdf_stringwidth pdf_stroke pdf_translate contained syn keyword phpFunctions pfpro_cleanup pfpro_init pfpro_process_raw pfpro_process pfpro_version contained -syn keyword phpFunctions pg_affected_rows pg_cancel_query pg_client_encoding pg_close pg_connect pg_connection_busy pg_connection_reset pg_connection_status pg_convert pg_copy_from pg_copy_to pg_dbname pg_delete pg_end_copy pg_escape_bytea pg_escape_string pg_fetch_all pg_fetch_array pg_fetch_assoc pg_fetch_object pg_fetch_result pg_fetch_row pg_field_is_null pg_field_name pg_field_num pg_field_prtlen pg_field_size pg_field_type pg_free_result pg_get_notify pg_get_pid pg_get_result pg_host pg_insert pg_last_error pg_last_notice pg_last_oid pg_lo_close pg_lo_create pg_lo_export pg_lo_import pg_lo_open pg_lo_read_all pg_lo_read pg_lo_seek pg_lo_tell pg_lo_unlink pg_lo_write pg_meta_data pg_num_fields pg_num_rows pg_options pg_pconnect pg_ping pg_port pg_put_line pg_query pg_result_error pg_result_seek pg_result_status pg_select pg_send_query pg_set_client_encoding pg_trace pg_tty pg_unescape_bytea pg_untrace pg_update contained -syn keyword phpFunctions posix_ctermid posix_get_last_error posix_getcwd posix_getegid posix_geteuid posix_getgid posix_getgrgid posix_getgrnam posix_getgroups posix_getlogin posix_getpgid posix_getpgrp posix_getpid posix_getppid posix_getpwnam posix_getpwuid posix_getrlimit posix_getsid posix_getuid posix_isatty posix_kill posix_mkfifo posix_setegid posix_seteuid posix_setgid posix_setpgid posix_setsid posix_setuid posix_strerror posix_times posix_ttyname posix_uname contained +syn keyword phpFunctions pg_affected_rows pg_cancel_query pg_client_encoding pg_close pg_connect pg_connection_busy pg_connection_reset pg_connection_status pg_convert pg_copy_from pg_copy_to pg_dbname pg_delete pg_end_copy pg_escape_bytea pg_escape_string pg_fetch_all pg_fetch_array pg_fetch_assoc pg_fetch_object pg_fetch_result pg_fetch_row pg_field_is_null pg_field_name pg_field_num pg_field_prtlen pg_field_size pg_field_type pg_free_result pg_get_notify pg_get_pid pg_get_result pg_host pg_insert pg_last_error pg_last_notice pg_last_oid pg_lo_close pg_lo_create pg_lo_export pg_lo_import pg_lo_open pg_lo_read_all pg_lo_read pg_lo_seek pg_lo_tell pg_lo_unlink pg_lo_write pg_meta_data pg_num_fields pg_num_rows pg_options pg_pconnect pg_ping pg_port pg_put_line pg_query pg_result_error pg_result_seek pg_result_status pg_select pg_send_query pg_set_client_encoding pg_trace pg_tty pg_unescape_bytea pg_untrace pg_update pg_clientencoding pg_cmdtuples pg_connect_poll pg_consume_input pg_errormessage pg_escape_identifier pg_escape_literal pg_exec pg_execute pg_fetch_all_columns pg_field_table pg_field_type_oid pg_fieldisnull pg_fieldname pg_fieldnum pg_fieldprtlen pg_fieldsize pg_fieldtype pg_flush pg_freeresult pg_getlastoid pg_lo_truncate pg_loclose pg_locreate pg_loexport pg_loimport pg_loopen pg_loread pg_loreadall pg_lounlink pg_lowrite pg_numfields pg_numrows pg_parameter_status pg_prepare pg_query_params pg_result pg_result_error_field pg_send_execute pg_send_prepare pg_send_query_params pg_set_error_verbosity pg_setclientencoding pg_socket pg_transaction_status pg_version contained +syn keyword phpFunctions posix_ctermid posix_get_last_error posix_getcwd posix_getegid posix_geteuid posix_getgid posix_getgrgid posix_getgrnam posix_getgroups posix_getlogin posix_getpgid posix_getpgrp posix_getpid posix_getppid posix_getpwnam posix_getpwuid posix_getrlimit posix_getsid posix_getuid posix_isatty posix_kill posix_mkfifo posix_setegid posix_seteuid posix_setgid posix_setpgid posix_setsid posix_setuid posix_strerror posix_times posix_ttyname posix_uname posix_access posix_errno posix_initgroups posix_mknod posix_setrlimit contained syn keyword phpFunctions printer_abort printer_close printer_create_brush printer_create_dc printer_create_font printer_create_pen printer_delete_brush printer_delete_dc printer_delete_font printer_delete_pen printer_draw_bmp printer_draw_chord printer_draw_elipse printer_draw_line printer_draw_pie printer_draw_rectangle printer_draw_roundrect printer_draw_text printer_end_doc printer_end_page printer_get_option printer_list printer_logical_fontheight printer_open printer_select_brush printer_select_font printer_select_pen printer_set_option printer_start_doc printer_start_page printer_write contained -syn keyword phpFunctions pspell_add_to_personal pspell_add_to_session pspell_check pspell_clear_session pspell_config_create pspell_config_ignore pspell_config_mode pspell_config_personal pspell_config_repl pspell_config_runtogether pspell_config_save_repl pspell_new_config pspell_new_personal pspell_new pspell_save_wordlist pspell_store_replacement pspell_suggest contained +syn keyword phpFunctions pspell_add_to_personal pspell_add_to_session pspell_check pspell_clear_session pspell_config_create pspell_config_ignore pspell_config_mode pspell_config_personal pspell_config_repl pspell_config_runtogether pspell_config_save_repl pspell_new_config pspell_new_personal pspell_new pspell_save_wordlist pspell_store_replacement pspell_suggest pspell_config_data_dir pspell_config_dict_dir contained syn keyword phpFunctions qdom_error qdom_tree contained -syn keyword phpFunctions readline_add_history readline_clear_history readline_completion_function readline_info readline_list_history readline_read_history readline_write_history readline contained +syn keyword phpFunctions readline_add_history readline_clear_history readline_completion_function readline_info readline_list_history readline_read_history readline_write_history readline readline_callback_handler_install readline_callback_handler_remove readline_callback_read_char readline_on_new_line readline_redisplay contained syn keyword phpFunctions recode_file recode_string recode contained syn keyword phpFunctions ereg_replace ereg eregi_replace eregi split spliti sql_regcase contained -syn keyword phpFunctions ftok msg_get_queue msg_receive msg_remove_queue msg_send msg_set_queue msg_stat_queue sem_acquire sem_get sem_release sem_remove shm_attach shm_detach shm_get_var shm_put_var shm_remove_var shm_remove contained +syn keyword phpFunctions ftok msg_get_queue msg_queue_exists msg_receive msg_remove_queue msg_send msg_set_queue msg_stat_queue sem_acquire sem_get sem_release sem_remove shm_attach shm_detach shm_get_var shm_has_var shm_put_var shm_remove_var shm_remove contained syn keyword phpFunctions sesam_affected_rows sesam_commit sesam_connect sesam_diagnostic sesam_disconnect sesam_errormsg sesam_execimm sesam_fetch_array sesam_fetch_result sesam_fetch_row sesam_field_array sesam_field_name sesam_free_result sesam_num_fields sesam_query sesam_rollback sesam_seek_row sesam_settransaction contained -syn keyword phpFunctions session_cache_expire session_cache_limiter session_decode session_destroy session_encode session_get_cookie_params session_id session_is_registered session_module_name session_name session_regenerate_id session_register session_save_path session_set_cookie_params session_set_save_handler session_start session_unregister session_unset session_write_close contained +syn keyword phpFunctions session_cache_expire session_cache_limiter session_decode session_destroy session_encode session_get_cookie_params session_id session_is_registered session_module_name session_name session_regenerate_id session_register session_save_path session_set_cookie_params session_set_save_handler session_start session_unregister session_unset session_write_close session_abort session_commit session_create_id session_gc session_register_shutdown session_reset session_status contained +syn keyword phpFunctions simplexml_import_dom simplexml_load_file simplexml_load_string contained syn keyword phpFunctions shmop_close shmop_delete shmop_open shmop_read shmop_size shmop_write contained syn keyword phpFunctions snmp_get_quick_print snmp_set_quick_print snmpget snmprealwalk snmpset snmpwalk snmpwalkoid contained -syn keyword phpFunctions socket_accept socket_bind socket_clear_error socket_close socket_connect socket_create_listen socket_create_pair socket_create socket_get_option socket_getpeername socket_getsockname socket_iovec_add socket_iovec_alloc socket_iovec_delete socket_iovec_fetch socket_iovec_free socket_iovec_set socket_last_error socket_listen socket_read socket_readv socket_recv socket_recvfrom socket_recvmsg socket_select socket_send socket_sendmsg socket_sendto socket_set_block socket_set_nonblock socket_set_option socket_shutdown socket_strerror socket_write socket_writev contained +syn keyword phpFunctions is_soap_fault use_soap_error_handler contained +syn keyword phpFunctions socket_accept socket_bind socket_clear_error socket_close socket_connect socket_create_listen socket_create_pair socket_create socket_get_option socket_getpeername socket_getsockname socket_iovec_add socket_iovec_alloc socket_iovec_delete socket_iovec_fetch socket_iovec_free socket_iovec_set socket_last_error socket_listen socket_read socket_readv socket_recv socket_recvfrom socket_recvmsg socket_select socket_send socket_sendmsg socket_sendto socket_set_block socket_set_nonblock socket_set_option socket_shutdown socket_strerror socket_write socket_writev socket_addrinfo_bind socket_addrinfo_connect socket_addrinfo_explain socket_addrinfo_lookup socket_cmsg_space socket_export_stream socket_getopt socket_import_stream socket_setopt contained +syn keyword phpFunctions class_implements class_parents class_uses iterator_apply iterator_count iterator_to_array spl_autoload spl_autoload_call spl_autoload_extensions spl_autoload_functions spl_autoload_register spl_autoload_unregister spl_classes spl_object_hash spl_object_id contained syn keyword phpFunctions sqlite_array_query sqlite_busy_timeout sqlite_changes sqlite_close sqlite_column sqlite_create_aggregate sqlite_create_function sqlite_current sqlite_error_string sqlite_escape_string sqlite_fetch_array sqlite_fetch_single sqlite_fetch_string sqlite_field_name sqlite_has_more sqlite_last_error sqlite_last_insert_rowid sqlite_libencoding sqlite_libversion sqlite_next sqlite_num_fields sqlite_num_rows sqlite_open sqlite_popen sqlite_query sqlite_rewind sqlite_seek sqlite_udf_decode_binary sqlite_udf_encode_binary sqlite_unbuffered_query contained syn keyword phpFunctions stream_context_create stream_context_get_options stream_context_set_option stream_context_set_params stream_copy_to_stream stream_filter_append stream_filter_prepend stream_filter_register stream_get_contents stream_get_filters stream_get_line stream_get_meta_data stream_get_transports stream_get_wrappers stream_register_wrapper stream_select stream_set_blocking stream_set_timeout stream_set_write_buffer stream_socket_accept stream_socket_client stream_socket_get_name stream_socket_recvfrom stream_socket_sendto stream_socket_server stream_wrapper_register contained syn keyword phpFunctions addcslashes addslashes bin2hex chop chr chunk_split convert_cyr_string count_chars crc32 crypt explode fprintf get_html_translation_table hebrev hebrevc html_entity_decode htmlentities htmlspecialchars implode join levenshtein localeconv ltrim md5_file md5 metaphone money_format nl_langinfo nl2br number_format ord parse_str print printf quoted_printable_decode quotemeta rtrim setlocale sha1_file sha1 similar_text soundex sprintf sscanf str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split str_word_count strcasecmp strchr strcmp strcoll strcspn strip_tags stripcslashes stripos stripslashes stristr strlen strnatcasecmp strnatcmp strncasecmp strncmp strpos strrchr strrev strripos strrpos strspn strstr strtok strtolower strtoupper strtr substr_compare substr_count substr_replace substr trim ucfirst ucwords vprintf vsprintf wordwrap contained syn keyword phpFunctions swf_actiongeturl swf_actiongotoframe swf_actiongotolabel swf_actionnextframe swf_actionplay swf_actionprevframe swf_actionsettarget swf_actionstop swf_actiontogglequality swf_actionwaitforframe swf_addbuttonrecord swf_addcolor swf_closefile swf_definebitmap swf_definefont swf_defineline swf_definepoly swf_definerect swf_definetext swf_endbutton swf_enddoaction swf_endshape swf_endsymbol swf_fontsize swf_fontslant swf_fonttracking swf_getbitmapinfo swf_getfontinfo swf_getframe swf_labelframe swf_lookat swf_modifyobject swf_mulcolor swf_nextid swf_oncondition swf_openfile swf_ortho2 swf_ortho swf_perspective swf_placeobject swf_polarview swf_popmatrix swf_posround swf_pushmatrix swf_removeobject swf_rotate swf_scale swf_setfont swf_setframe swf_shapearc swf_shapecurveto3 swf_shapecurveto swf_shapefillbitmapclip swf_shapefillbitmaptile swf_shapefilloff swf_shapefillsolid swf_shapelinesolid swf_shapelineto swf_shapemoveto swf_showframe swf_startbutton swf_startdoaction swf_startshape swf_startsymbol swf_textwidth swf_translate swf_viewport contained syn keyword phpFunctions sybase_affected_rows sybase_close sybase_connect sybase_data_seek sybase_deadlock_retry_count sybase_fetch_array sybase_fetch_assoc sybase_fetch_field sybase_fetch_object sybase_fetch_row sybase_field_seek sybase_free_result sybase_get_last_message sybase_min_client_severity sybase_min_error_severity sybase_min_message_severity sybase_min_server_severity sybase_num_fields sybase_num_rows sybase_pconnect sybase_query sybase_result sybase_select_db sybase_set_message_handler sybase_unbuffered_query contained -syn keyword phpFunctions tidy_access_count tidy_clean_repair tidy_config_count tidy_diagnose tidy_error_count tidy_get_body tidy_get_config tidy_get_error_buffer tidy_get_head tidy_get_html_ver tidy_get_html tidy_get_output tidy_get_release tidy_get_root tidy_get_status tidy_getopt tidy_is_xhtml tidy_load_config tidy_parse_file tidy_parse_string tidy_repair_file tidy_repair_string tidy_reset_config tidy_save_config tidy_set_encoding tidy_setopt tidy_warning_count contained +syn keyword phpFunctions tidy_access_count tidy_clean_repair tidy_config_count tidy_diagnose tidy_error_count tidy_get_body tidy_get_config tidy_get_error_buffer tidy_get_head tidy_get_html_ver tidy_get_html tidy_get_output tidy_get_release tidy_get_root tidy_get_status tidy_getopt tidy_is_xhtml tidy_load_config tidy_parse_file tidy_parse_string tidy_repair_file tidy_repair_string tidy_reset_config tidy_save_config tidy_set_encoding tidy_setopt tidy_warning_count tidy_is_xml tidy_get_opt_doc contained syn keyword phpMethods attributes children get_attr get_nodes has_children has_siblings is_asp is_comment is_html is_jsp is_jste is_text is_xhtml is_xml next prev tidy_node contained syn keyword phpFunctions token_get_all token_name contained syn keyword phpFunctions base64_decode base64_encode get_meta_tags http_build_query parse_url rawurldecode rawurlencode urldecode urlencode contained syn keyword phpFunctions doubleval empty floatval get_defined_vars get_resource_type gettype import_request_variables intval is_array is_bool is_callable is_double is_float is_int is_integer is_long is_null is_numeric is_object is_real is_resource is_scalar is_string isset print_r serialize settype strval unserialize unset var_dump var_export contained +syn keyword phpFunctions get_called_class property_exists interface_exists trait_exists class_alias get_mangled_object_vars set_exception_handler restore_exception_handler get_declared_traits get_declared_interfaces get_resources gc_mem_caches gc_collect_cycles gc_enabled gc_enable gc_disable gc_status contained syn keyword phpFunctions vpopmail_add_alias_domain_ex vpopmail_add_alias_domain vpopmail_add_domain_ex vpopmail_add_domain vpopmail_add_user vpopmail_alias_add vpopmail_alias_del_domain vpopmail_alias_del vpopmail_alias_get_all vpopmail_alias_get vpopmail_auth_user vpopmail_del_domain_ex vpopmail_del_domain vpopmail_del_user vpopmail_error vpopmail_passwd vpopmail_set_user_quota contained syn keyword phpFunctions w32api_deftype w32api_init_dtype w32api_invoke_function w32api_register_function w32api_set_call_method contained syn keyword phpFunctions wddx_add_vars wddx_deserialize wddx_packet_end wddx_packet_start wddx_serialize_value wddx_serialize_vars contained syn keyword phpFunctions utf8_decode utf8_encode xml_error_string xml_get_current_byte_index xml_get_current_column_number xml_get_current_line_number xml_get_error_code xml_parse_into_struct xml_parse xml_parser_create_ns xml_parser_create xml_parser_free xml_parser_get_option xml_parser_set_option xml_set_character_data_handler xml_set_default_handler xml_set_element_handler xml_set_end_namespace_decl_handler xml_set_external_entity_ref_handler xml_set_notation_decl_handler xml_set_object xml_set_processing_instruction_handler xml_set_start_namespace_decl_handler xml_set_unparsed_entity_decl_handler contained -syn keyword phpFunctions xmlrpc_decode_request xmlrpc_decode xmlrpc_encode_request xmlrpc_encode xmlrpc_get_type xmlrpc_parse_method_descriptions xmlrpc_server_add_introspection_data xmlrpc_server_call_method xmlrpc_server_create xmlrpc_server_destroy xmlrpc_server_register_introspection_callback xmlrpc_server_register_method xmlrpc_set_type contained +syn keyword phpFunctions xmlrpc_decode_request xmlrpc_decode xmlrpc_encode_request xmlrpc_encode xmlrpc_get_type xmlrpc_parse_method_descriptions xmlrpc_server_add_introspection_data xmlrpc_server_call_method xmlrpc_server_create xmlrpc_server_destroy xmlrpc_server_register_introspection_callback xmlrpc_server_register_method xmlrpc_set_type xmlrpc_is_fault contained +syn keyword phpFunctions xmlwriter_end_attribute xmlwriter_end_cdata xmlwriter_end_comment xmlwriter_end_document xmlwriter_end_dtd xmlwriter_end_dtd_attlist xmlwriter_end_dtd_element xmlwriter_end_dtd_entity xmlwriter_end_element xmlwriter_end_pi xmlwriter_flush xmlwriter_full_end_element xmlwriter_open_memory xmlwriter_open_uri xmlwriter_output_memory xmlwriter_set_indent xmlwriter_set_indent_string xmlwriter_start_attribute xmlwriter_start_attribute_ns xmlwriter_start_cdata xmlwriter_start_comment xmlwriter_start_document xmlwriter_start_dtd xmlwriter_start_dtd_attlist xmlwriter_start_dtd_element xmlwriter_start_dtd_entity xmlwriter_start_element xmlwriter_start_element_ns xmlwriter_start_pi xmlwriter_text xmlwriter_write_attribute xmlwriter_write_attribute_ns xmlwriter_write_cdata xmlwriter_write_comment xmlwriter_write_dtd xmlwriter_write_dtd_attlist xmlwriter_write_dtd_element xmlwriter_write_dtd_entity xmlwriter_write_element xmlwriter_write_element_ns xmlwriter_write_pi xmlwriter_write_raw contained syn keyword phpFunctions xslt_create xslt_errno xslt_error xslt_free xslt_output_process xslt_set_base xslt_set_encoding xslt_set_error_handler xslt_set_log xslt_set_sax_handler xslt_set_sax_handlers xslt_set_scheme_handler xslt_set_scheme_handlers contained syn keyword phpFunctions yaz_addinfo yaz_ccl_conf yaz_ccl_parse yaz_close yaz_connect yaz_database yaz_element yaz_errno yaz_error yaz_es_result yaz_get_option yaz_hits yaz_itemorder yaz_present yaz_range yaz_record yaz_scan_result yaz_scan yaz_schema yaz_search yaz_set_option yaz_sort yaz_syntax yaz_wait contained syn keyword phpFunctions zip_close zip_entry_close zip_entry_compressedsize zip_entry_compressionmethod zip_entry_filesize zip_entry_name zip_entry_open zip_entry_read zip_open zip_read contained -syn keyword phpFunctions gzclose gzcompress gzdeflate gzencode gzeof gzfile gzgetc gzgets gzgetss gzinflate gzopen gzpassthru gzputs gzread gzrewind gzseek gztell gzuncompress gzwrite readgzfile zlib_get_coding_type contained +syn keyword phpFunctions gzclose gzcompress gzdeflate gzencode gzeof gzfile gzgetc gzgets gzgetss gzinflate gzopen gzpassthru gzputs gzread gzrewind gzseek gztell gzuncompress gzwrite readgzfile zlib_get_coding_type gzdecode zlib_encode zlib_decode deflate_init deflate_add inflate_init inflate_add inflate_get_status inflate_get_read_len contained if exists( "php_baselib" ) syn keyword phpMethods query next_record num_rows affected_rows nf f p np num_fields haltmsg seek link_id query_id metadata table_names nextid connect halt free register unregister is_registered delete url purl self_url pself_url hidden_session add_query padd_query reimport_get_vars reimport_post_vars reimport_cookie_vars set_container set_tokenname release_token put_headers get_id get_id put_id freeze thaw gc reimport_any_vars start url purl login_if is_authenticated auth_preauth auth_loginform auth_validatelogin auth_refreshlogin auth_registerform auth_doregister start check have_perm permsum perm_invalid contained @@ -246,12 +267,12 @@ if exists( "php_baselib" ) endif " Conditional -syn keyword phpConditional declare else enddeclare endswitch elseif endif if switch contained +syn keyword phpConditional declare else enddeclare endswitch elseif endif if switch match contained " Repeat syn keyword phpRepeat as do endfor endforeach endwhile for foreach while contained -" Repeat +" Label syn keyword phpLabel case default switch contained " Statement @@ -275,7 +296,8 @@ syn match phpOperator "&&\|\<and\>" contained display syn match phpOperator "||\|\<x\=or\>" contained display syn match phpRelation "[!=<>]=" contained display syn match phpRelation "[<>]" contained display -syn match phpMemberSelector "->" contained display +" PHP 8.0 adds the nullsafe operator ?-> for property access and method calls. +syn match phpMemberSelector "?\?->" contained display syn match phpVarSelector "\$" contained display " Identifier @@ -285,37 +307,37 @@ syn region phpIdentifierComplex matchgroup=phpParent start="{\$"rs=e-1 end="}" c syn region phpIdentifierComplexP matchgroup=phpParent start="\[" end="]" contains=@phpClInside contained " Interpolated indentifiers (inside strings) - syn match phpBrackets "[][}{]" contained display - " errors - syn match phpInterpSimpleError "\[[^]]*\]" contained display " fallback (if nothing else matches) - syn match phpInterpSimpleError "->[^a-zA-Z_]" contained display - " make sure these stay above the correct DollarCurlies so they don't take priority - syn match phpInterpBogusDollarCurley "${[^}]*}" contained display " fallback (if nothing else matches) - syn match phpinterpSimpleBracketsInner "\w\+" contained - syn match phpInterpSimpleBrackets "\[\h\w*]" contained contains=phpBrackets,phpInterpSimpleBracketsInner - syn match phpInterpSimpleBrackets "\[\d\+]" contained contains=phpBrackets,phpInterpSimpleBracketsInner - syn match phpInterpSimpleBrackets "\[0[xX]\x\+]" contained contains=phpBrackets,phpInterpSimpleBracketsInner - syn match phpInterpSimple "\$\h\w*\(\[[^]]*\]\|->\h\w*\)\?" contained contains=phpInterpSimpleBrackets,phpIdentifier,phpInterpSimpleError,phpMethods,phpMemberSelector display - syn match phpInterpVarname "\h\w*" contained - syn match phpInterpMethodName "\h\w*" contained " default color - syn match phpInterpSimpleCurly "\${\h\w*}" contains=phpInterpVarname contained extend - syn region phpInterpDollarCurley1Helper matchgroup=phpParent start="{" end="\[" contains=phpInterpVarname contained - syn region phpInterpDollarCurly1 matchgroup=phpParent start="\${\h\w*\["rs=s+1 end="]}" contains=phpInterpDollarCurley1Helper,@phpClConst contained extend - - syn match phpInterpDollarCurley2Helper "{\h\w*->" contains=phpBrackets,phpInterpVarname,phpMemberSelector contained - - syn region phpInterpDollarCurly2 matchgroup=phpParent start="\${\h\w*->"rs=s+1 end="}" contains=phpInterpDollarCurley2Helper,phpInterpMethodName contained - - syn match phpInterpBogusDollarCurley "${\h\w*->}" contained display - syn match phpInterpBogusDollarCurley "${\h\w*\[]}" contained display - - syn region phpInterpComplex matchgroup=phpParent start="{\$"rs=e-1 end="}" contains=phpIdentifier,phpMemberSelector,phpVarSelector,phpIdentifierComplexP contained extend - syn region phpIdentifierComplexP matchgroup=phpParent start="\[" end="]" contains=@phpClInside contained - " define a cluster to get all interpolation syntaxes for double-quoted strings - syn cluster phpInterpDouble contains=phpInterpSimple,phpInterpSimpleCurly,phpInterpDollarCurly1,phpInterpDollarCurly2,phpInterpBogusDollarCurley,phpInterpComplex - -" Methoden -syn match phpMethodsVar "->\h\w*" contained contains=phpMethods,phpMemberSelector display + syn match phpBrackets "[][}{]" contained display + " errors + syn match phpInterpSimpleError "\[[^]]*\]" contained display " fallback (if nothing else matches) + syn match phpInterpSimpleError "?\?->[^a-zA-Z_]" contained display + " make sure these stay above the correct DollarCurlies so they don't take priority + syn match phpInterpBogusDollarCurley "${[^}]*}" contained display " fallback (if nothing else matches) + syn match phpinterpSimpleBracketsInner "\w\+" contained + syn match phpInterpSimpleBrackets "\[\h\w*]" contained contains=phpBrackets,phpInterpSimpleBracketsInner + syn match phpInterpSimpleBrackets "\[\d\+]" contained contains=phpBrackets,phpInterpSimpleBracketsInner + syn match phpInterpSimpleBrackets "\[0[xX]\x\+]" contained contains=phpBrackets,phpInterpSimpleBracketsInner + syn match phpInterpSimple "\$\h\w*\(\[[^]]*\]\|?\?->\h\w*\)\?" contained contains=phpInterpSimpleBrackets,phpIdentifier,phpInterpSimpleError,phpMethods,phpMemberSelector display + syn match phpInterpVarname "\h\w*" contained + syn match phpInterpMethodName "\h\w*" contained " default color + syn match phpInterpSimpleCurly "\${\h\w*}" contains=phpInterpVarname contained extend + syn region phpInterpDollarCurley1Helper matchgroup=phpParent start="{" end="\[" contains=phpInterpVarname contained + syn region phpInterpDollarCurly1 matchgroup=phpParent start="\${\h\w*\["rs=s+1 end="]}" contains=phpInterpDollarCurley1Helper,@phpClConst contained extend + + syn match phpInterpDollarCurley2Helper "{\h\w*?\?->" contains=phpBrackets,phpInterpVarname,phpMemberSelector contained + + syn region phpInterpDollarCurly2 matchgroup=phpParent start="\${\h\w*?\?->"rs=s+1 end="}" contains=phpInterpDollarCurley2Helper,phpInterpMethodName contained + + syn match phpInterpBogusDollarCurley "${\h\w*?\?->}" contained display + syn match phpInterpBogusDollarCurley "${\h\w*\[]}" contained display + + syn region phpInterpComplex matchgroup=phpParent start="{\$"rs=e-1 end="}" contains=phpIdentifier,phpMemberSelector,phpVarSelector,phpIdentifierComplexP contained extend + syn region phpIdentifierComplexP matchgroup=phpParent start="\[" end="]" contains=@phpClInside contained + " define a cluster to get all interpolation syntaxes for double-quoted strings + syn cluster phpInterpDouble contains=phpInterpSimple,phpInterpSimpleCurly,phpInterpDollarCurly1,phpInterpDollarCurly2,phpInterpBogusDollarCurley,phpInterpComplex + +" Methods +syn match phpMethodsVar "?\?->\h\w*" contained contains=phpMethods,phpMemberSelector display " Include syn keyword phpInclude include require include_once require_once use contained @@ -326,24 +348,28 @@ syn keyword phpDefine new clone contained " Boolean syn keyword phpBoolean true false contained -" Number -syn match phpNumber "-\=\<\d\+\>" contained display -syn match phpNumber "\<0x\x\{1,8}\>" contained display - " Float -syn match phpFloat "\(-\=\<\d+\|-\=\)\.\d\+\>" contained display +" Refer to: https://www.php.net/manual/en/language.types.float.php +syn match phpFloat "\%(\w\|\.\)\@<!\%(\d_\?\|\.\)*\d\%(\d\|_\|\.\)*\%([eE][+-]\=\%(\d\|_\|\.\)\+\)\=\%(\w\|\.\)\@!" contained contains=phpFloatError display +syn match phpFloatError "\%([eE.][0-9._+-]*\.\|__\|_\(\>\|[eE]\)\|\(\>\|[eE]\)_\)" contained display + +" Number +syn match phpNumber "\%(\.\)\@<!\<\%([1-9]\d*\|0\|0[xX]\(\x_\?\)*\x\)\>\%(\.\)\@!" contained display +syn match phpNumber "\%(\.\)\@<!\<0\d\+\>\%(\.\)\@!" contained contains=phpOctalError display +syn match phpBinaryError "[2-9]" contained display +syn match phpNumber "\%(\.\)\@<!\<0[bB]\(\d_\?\)*\d\>\%(\.\)\@!" contained contains=phpBinaryError display " Backslash escapes - syn case match - " for double quotes and heredoc - syn match phpBackslashSequences "\\[fnrtv\\\"$]" contained display - syn match phpBackslashSequences "\\\d\{1,3}" contained contains=phpOctalError display - syn match phpBackslashSequences "\\x\x\{1,2}" contained display - " additional sequence for double quotes only - syn match phpBackslashDoubleQuote "\\[\"]" contained display - " for single quotes only - syn match phpBackslashSingleQuote "\\[\\']" contained display - syn case ignore +syn case match +" for double quotes and heredoc +syn match phpBackslashSequences "\\[fnrtv\\\"$]" contained display +syn match phpBackslashSequences "\\\d\{1,3}" contained contains=phpOctalError display +syn match phpBackslashSequences "\\x\x\{1,2}" contained display +" additional sequence for double quotes only +syn match phpBackslashDoubleQuote "\\[\"]" contained display +" for single quotes only +syn match phpBackslashSingleQuote "\\[\\']" contained display +syn case ignore " Error @@ -379,30 +405,31 @@ endif syn case match " HereDoc -syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\I\i*\)\2$" end="^\z1\(;\=$\)\@=" contained contains=phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend +syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\I\i*\)\2$" end="^\s*\z1\>" contained contains=phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend " including HTML,JavaScript,SQL even if not enabled via options -syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(html\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@htmlTop,phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend -syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(sql\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@sqlTop,phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend -syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(javascript\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@htmlJavascript,phpIdentifierSimply,phpIdentifier,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend +syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(html\)\c\(\i*\)\)\2$" end="^\s*\z1\>" contained contains=@htmlTop,phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend +syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(sql\)\c\(\i*\)\)\2$" end="^\s*\z1\>" contained contains=@sqlTop,phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend +syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(javascript\)\c\(\i*\)\)\2$" end="^\s*\z1\>" contained contains=@htmlJavascript,phpIdentifierSimply,phpIdentifier,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend " NowDoc -syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\I\i*\)'$" end="^\z1\(;\=$\)\@=" contained contains=@Spell keepend extend +syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\I\i*\)'$" end="^\s*\z1\>" contained contains=@Spell keepend extend " including HTML,JavaScript,SQL even if not enabled via options -syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(html\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@htmlTop,@Spell keepend extend -syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(sql\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@sqlTop,@Spell keepend extend -syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(javascript\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@htmlJavascript,@Spell keepend extend +syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(html\)\c\(\i*\)\)'$" end="^\s*\z1\>" contained contains=@htmlTop,@Spell keepend extend +syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(sql\)\c\(\i*\)\)'$" end="^\s*\z1\>" contained contains=@sqlTop,@Spell keepend extend +syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(javascript\)\c\(\i*\)\)'$" end="^\s*\z1\>" contained contains=@htmlJavascript,@Spell keepend extend syn case ignore " Parent if exists("php_parent_error_close") || exists("php_parent_error_open") syn match phpParent "[{}]" contained syn region phpParent matchgroup=Delimiter start="(" end=")" contained contains=@phpClInside transparent - syn region phpParent matchgroup=Delimiter start="\[" end="\]" contained contains=@phpClInside transparent + syn region phpParent matchgroup=Delimiter start="#\?\[" end="\]" contained contains=@phpClInside transparent if !exists("php_parent_error_close") syn match phpParent "[\])]" contained endif else syn match phpParent "[({[\]})]" contained + syn match phpParent "#\[" contained endif syn cluster phpClConst contains=phpFunctions,phpIdentifier,phpConditional,phpRepeat,phpStatement,phpOperator,phpRelation,phpStringSingle,phpStringDouble,phpBacktick,phpNumber,phpFloat,phpKeyword,phpType,phpBoolean,phpStructure,phpMethodsVar,phpConstant,phpCoreConstant,phpException @@ -438,6 +465,7 @@ if exists("php_folding") && php_folding==1 " match one line constructs here and skip them at folding syn keyword phpSCKeyword abstract final private protected public static contained syn keyword phpFCKeyword function contained + syn keyword phpDefine fn contained syn keyword phpStorageClass global contained syn match phpDefine "\(\s\|^\)\(abstract\s\+\|final\s\+\|private\s\+\|protected\s\+\|public\s\+\|static\s\+\)*function\(\s\+.*[;}]\)\@=" contained contains=phpSCKeyword syn match phpStructure "\(\s\|^\)\(abstract\s\+\|final\s\+\)*\(trait\|class\)\(\s\+.*}\)\@=" contained @@ -455,7 +483,7 @@ if exists("php_folding") && php_folding==1 syn region phpFoldCatch matchgroup=Exception start="^\z(\s*\)catch\s\+\([^}]*$\)\@=" matchgroup=Delimiter end="^\z1}" contains=@phpClFunction,phpFoldFunction contained transparent fold extend syn region phpFoldTry matchgroup=Exception start="^\z(\s*\)try\s\+\([^}]*$\)\@=" matchgroup=Delimiter end="^\z1}" contains=@phpClFunction,phpFoldFunction contained transparent fold extend else - syn keyword phpDefine function contained + syn keyword phpDefine function fn contained syn keyword phpStructure abstract class trait interface contained syn keyword phpException catch throw try finally contained syn keyword phpStorageClass final global private protected public static contained @@ -489,16 +517,22 @@ hi def link phpSpecialFunction phpOperator " Highlighting for PHP5's built-in classes " - built-in classes harvested from get_declared_classes() in 5.1.4 syntax keyword phpClasses containedin=ALLBUT,phpComment,phpStringDouble,phpStringSingle,phpIdentifier,phpMethodsVar - \ stdClass __PHP_Incomplete_Class php_user_filter Directory ArrayObject + \ stdClass __PHP_Incomplete_Class php_user_filter AssertionError Directory ArrayObject \ Exception ErrorException LogicException BadFunctionCallException BadMethodCallException DomainException + \ ArgumentCountError ArithmeticError ClosedGeneratorException Closure CompileError DivisionByZeroError Generator ParseError TypeError WeakReference \ RecursiveIteratorIterator IteratorIterator FilterIterator RecursiveFilterIterator ParentIterator LimitIterator \ CachingIterator RecursiveCachingIterator NoRewindIterator AppendIterator InfiniteIterator EmptyIterator \ ArrayIterator RecursiveArrayIterator DirectoryIterator RecursiveDirectoryIterator + \ CallbackFilterIterator FilesystemIterator GlobIterator MultipleIterator RecursiveCallbackFilterIterator + \ RecursiveRegexIterator RecursiveTreeIterator RegexIterator SplDoublyLinkedList + \ SplFixedArray SplHeap SplMaxHeap SplMinHeap SplPriorityQueue SplQueue SplStack \ InvalidArgumentException LengthException OutOfRangeException RuntimeException OutOfBoundsException \ OverflowException RangeException UnderflowException UnexpectedValueException \ PDO PDOException PDOStatement PDORow \ Reflection ReflectionFunction ReflectionParameter ReflectionMethod ReflectionClass \ ReflectionObject ReflectionProperty ReflectionExtension ReflectionException + \ ReflectionClassConstant ReflectionFunctionAbstract ReflectionGenerator ReflectionNamedType + \ ReflectionReference ReflectionType ReflectionZendExtension \ SplFileInfo SplFileObject SplTempFileObject SplObjectStorage \ XMLWriter LibXMLError XMLReader SimpleXMLElement SimpleXMLIterator \ DOMException DOMStringList DOMNameList DOMDomError DOMErrorHandler @@ -507,13 +541,33 @@ syntax keyword phpClasses containedin=ALLBUT,phpComment,phpStringDouble,phpStrin \ DOMCharacterData DOMAttr DOMElement DOMText DOMComment DOMTypeinfo DOMUserDataHandler \ DOMLocator DOMConfiguration DOMCdataSection DOMDocumentType DOMNotation DOMEntity \ DOMEntityReference DOMProcessingInstruction DOMStringExtend DOMXPath + \ APCIterator APCuIterator + \ CURLFile + \ DateInterval DatePeriod DateTime DateTimeImmutable DateTimeZone + \ finfo + \ GMP + \ Collator IntlBreakIterator IntlCalendar IntlChar IntlCodePointBreakIterator IntlDateFormatter IntlException IntlGregorianCalendar IntlIterator IntlPartsIterator IntlRuleBasedBreakIterator IntlTimeZone Locale MessageFormatter Normalizer NumberFormatter ResourceBundle Spoofchecker Transliterator UConverter + \ FFI CData CType ParserException + \ HashContext + \ JsonException + \ Memcached MemcachedException + \ mysqli mysqli_driver mysqli_result mysqli_sql_exception mysqli_stmt mysqli_warning + \ SessionHandler + \ SoapClient SoapFault SoapHeader SoapParam SoapServer SoapVar + \ SQLite3 SQLite3Result SQLite3Stmt + \ tidy tidyNode + \ XSLTProcessor ZipArchive + \ Phar PharData PharException PharFileInfo + hi def link phpClasses phpFunctions -" Highlighting for PHP5's built-in interfaces -" - built-in classes harvested from get_declared_interfaces() in 5.1.4 +" Highlighting for PHP's built-in interfaces syntax keyword phpInterfaces containedin=ALLBUT,phpComment,phpStringDouble,phpStringSingle,phpIdentifier,phpMethodsVar \ Iterator IteratorAggregate RecursiveIterator OuterIterator SeekableIterator \ Traversable ArrayAccess Serializable Countable SplObserver SplSubject Reflector + \ Throwable DateTimeInterface JsonSerializable SessionHandlerInterface SessionIdInterface SessionUpdateTimestampHandlerInterface + \ + hi def link phpInterfaces phpConstant " option defaults: @@ -535,6 +589,7 @@ if php_special_functions " - eval() is the token 'make_your_code_twice_as_complex()' function for PHP. " - user_error()/trigger_error() can be overloaded by set_error_handler and also " have the capacity to terminate your script when type is E_USER_ERROR. + " - match(){} is not a function syntax keyword phpSpecialFunction containedin=ALLBUT,phpComment,phpStringDouble,phpStringSingle \ user_error trigger_error isset unset eval extract compact empty endif @@ -623,6 +678,8 @@ hi def link phpBrackets Delimiter hi def link phpIdentifierConst Delimiter hi def link phpParentError Error hi def link phpOctalError Error +hi def link phpBinaryError Error +hi def link phpFloatError Error hi def link phpInterpSimpleError Error hi def link phpInterpBogusDollarCurley Error hi def link phpInterpDollarCurly1 Error diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 6f0818c845..1a37af1c8a 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -398,7 +398,7 @@ syn match vimMenuBang "!" contained skipwhite nextgroup=@vimMenuList " Angle-Bracket Notation (tnx to Michael Geddes) {{{2 " ====================== syn case ignore -syn match vimNotation "\%#=1\(\\\|<lt>\)\=<\([scamd]-\)\{0,4}x\=\(f\d\{1,2}\|[^ \t:]\|cmd\|cr\|lf\|linefeed\|return\|k\=del\%[ete]\|bs\|backspace\|tab\|esc\|right\|left\|help\|undo\|insert\|ins\|mouse\|k\=home\|k\=end\|kplus\|kminus\|kdivide\|kmultiply\|kenter\|kpoint\|space\|k\=\(page\)\=\(\|down\|up\|k\d\>\)\)>" contains=vimBracket +syn match vimNotation "\%#=1\(\\\|<lt>\)\=<\([scamd]-\)\{0,4}x\=\(f\d\{1,2}\|[^ \t:]\|cmd\|cr\|lf\|linefeed\|return\|enter\|k\=del\%[ete]\|bs\|backspace\|tab\|esc\|right\|left\|help\|undo\|insert\|ins\|mouse\|k\=home\|k\=end\|kplus\|kminus\|kdivide\|kmultiply\|kenter\|kpoint\|space\|k\=\(page\)\=\(\|down\|up\|k\d\>\)\)>" contains=vimBracket syn match vimNotation "\%#=1\(\\\|<lt>\)\=<\([scam2-4]-\)\{0,4}\(right\|left\|middle\)\(mouse\)\=\(drag\|release\)\=>" contains=vimBracket syn match vimNotation "\%#=1\(\\\|<lt>\)\=<\(bslash\|plug\|sid\|space\|bar\|nop\|nul\|lt\)>" contains=vimBracket syn match vimNotation '\(\\\|<lt>\)\=<C-R>[0-9a-z"%#:.\-=]'he=e-1 contains=vimBracket diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index b1a7f92854..0507e4b7b6 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -186,6 +186,7 @@ param_exclude = ( # Annotations are displayed as line items after API function descriptions. annotation_map = { 'FUNC_API_FAST': '{fast}', + 'FUNC_API_CHECK_TEXTLOCK': 'not allowed when |textlock| is active', } diff --git a/scripts/genvimvim.lua b/scripts/genvimvim.lua index 806533f2ff..ccd5489fdc 100644 --- a/scripts/genvimvim.lua +++ b/scripts/genvimvim.lua @@ -55,7 +55,7 @@ end vimcmd_start = 'syn keyword vimCommand contained ' w(vimcmd_start) local prev_cmd = nil -for _, cmd_desc in ipairs(ex_cmds) do +for _, cmd_desc in ipairs(ex_cmds.cmds) do if lld.line_length > 850 then w('\n' .. vimcmd_start) end diff --git a/scripts/update_version_stamp.lua b/scripts/update_version_stamp.lua index 11b521fab6..0342e08f31 100755 --- a/scripts/update_version_stamp.lua +++ b/scripts/update_version_stamp.lua @@ -35,11 +35,10 @@ if not described then end -- `git describe` annotates the most recent tagged release; for pre-release --- builds we must replace that with the unreleased version. -local with_prefix = described:gsub("^v%d+%.%d+%.%d+", prefix) -if described == with_prefix then - -- Prepend the prefix always, e.g. with "nightly-12208-g4041b62b9". - with_prefix = prefix .. "-" .. described +-- builds we append that to the dev version +local with_prefix = prefix +if prefix:match('-dev$') ~= nil then + with_prefix = prefix .. '+' .. described:gsub('^v%d+%.%d+%.%d+-', '') end -- Read existing include file. diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 8ec087c626..db77931c16 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -242,7 +242,7 @@ if(use_git_version) add_custom_target(update_version_stamp ALL COMMAND ${LUA_PRG} scripts/update_version_stamp.lua ${relbuild}/config/auto/versiondef_git.h - "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}" + "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}${NVIM_VERSION_PRERELEASE}" WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} BYPRODUCTS ${CMAKE_BINARY_DIR}/config/auto/versiondef_git.h) else() diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 1011f050fd..8d82d22040 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1,7 +1,7 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// Much of this code was adapted from 'if_py_both.h' from the original +// Some of this code was adapted from 'if_py_both.h' from the original // vim source #include <stdbool.h> #include <stdint.h> @@ -80,34 +80,6 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) return buf->b_ml.ml_line_count; } -/// Gets a buffer line -/// -/// @deprecated use nvim_buf_get_lines instead. -/// for positive indices (including 0) use -/// "nvim_buf_get_lines(buffer, index, index+1, true)" -/// for negative indices use -/// "nvim_buf_get_lines(buffer, index-1, index, true)" -/// -/// @param buffer Buffer handle -/// @param index Line index -/// @param[out] err Error details, if any -/// @return Line string -String buffer_get_line(Buffer buffer, Integer index, Error *err) -{ - String rv = { .size = 0 }; - - index = convert_index(index); - Array slice = nvim_buf_get_lines(0, buffer, index, index+1, true, err); - - if (!ERROR_SET(err) && slice.size) { - rv = slice.items[0].data.string; - } - - xfree(slice.items); - - return rv; -} - /// Activates buffer-update events on a channel, or as Lua callbacks. /// /// Example (Lua): capture buffer updates in a global `events` variable @@ -149,6 +121,8 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// - buffer handle /// - utf_sizes: include UTF-32 and UTF-16 size of the replaced /// region, as args to `on_lines`. +/// - preview: also attach to command preview (i.e. 'inccommand') +/// events. /// @param[out] err Error details, if any /// @return False if attach failed (invalid parameter, or buffer isn't loaded); /// otherwise True. TODO: LUA_API_NO_EVAL @@ -204,6 +178,12 @@ Boolean nvim_buf_attach(uint64_t channel_id, goto error; } cb.utf_sizes = v->data.boolean; + } else if (is_lua && strequal("preview", k.data)) { + if (v->type != kObjectTypeBoolean) { + api_set_error(err, kErrorTypeValidation, "preview must be boolean"); + goto error; + } + cb.preview = v->data.boolean; } else { api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); goto error; @@ -258,68 +238,6 @@ void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, redraw_buf_range_later(buf, (linenr_T)first+1, (linenr_T)last); } -/// Sets a buffer line -/// -/// @deprecated use nvim_buf_set_lines instead. -/// for positive indices use -/// "nvim_buf_set_lines(buffer, index, index+1, true, [line])" -/// for negative indices use -/// "nvim_buf_set_lines(buffer, index-1, index, true, [line])" -/// -/// @param buffer Buffer handle -/// @param index Line index -/// @param line Contents of the new line -/// @param[out] err Error details, if any -void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) -{ - Object l = STRING_OBJ(line); - Array array = { .items = &l, .size = 1 }; - index = convert_index(index); - nvim_buf_set_lines(0, buffer, index, index+1, true, array, err); -} - -/// Deletes a buffer line -/// -/// @deprecated use nvim_buf_set_lines instead. -/// for positive indices use -/// "nvim_buf_set_lines(buffer, index, index+1, true, [])" -/// for negative indices use -/// "nvim_buf_set_lines(buffer, index-1, index, true, [])" -/// @param buffer buffer handle -/// @param index line index -/// @param[out] err Error details, if any -void buffer_del_line(Buffer buffer, Integer index, Error *err) -{ - Array array = ARRAY_DICT_INIT; - index = convert_index(index); - nvim_buf_set_lines(0, buffer, index, index+1, true, array, err); -} - -/// Retrieves a line range from the buffer -/// -/// @deprecated use nvim_buf_get_lines(buffer, newstart, newend, false) -/// where newstart = start + int(not include_start) - int(start < 0) -/// newend = end + int(include_end) - int(end < 0) -/// int(bool) = 1 if bool is true else 0 -/// @param buffer Buffer handle -/// @param start First line index -/// @param end Last line index -/// @param include_start True if the slice includes the `start` parameter -/// @param include_end True if the slice includes the `end` parameter -/// @param[out] err Error details, if any -/// @return Array of lines -ArrayOf(String) buffer_get_line_slice(Buffer buffer, - Integer start, - Integer end, - Boolean include_start, - Boolean include_end, - Error *err) -{ - start = convert_index(start) + !include_start; - end = convert_index(end) + include_end; - return nvim_buf_get_lines(0, buffer, start , end, false, err); -} - /// Gets a line-range from the buffer. /// /// Indexing is zero-based, end-exclusive. Negative indices are interpreted @@ -391,36 +309,28 @@ end: return rv; } - -/// Replaces a line range on the buffer -/// -/// @deprecated use nvim_buf_set_lines(buffer, newstart, newend, false, lines) -/// where newstart = start + int(not include_start) + int(start < 0) -/// newend = end + int(include_end) + int(end < 0) -/// int(bool) = 1 if bool is true else 0 -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param start First line index -/// @param end Last line index -/// @param include_start True if the slice includes the `start` parameter -/// @param include_end True if the slice includes the `end` parameter -/// @param replacement Array of lines to use as replacement (0-length -// array will delete the line range) -/// @param[out] err Error details, if any -void buffer_set_line_slice(Buffer buffer, - Integer start, - Integer end, - Boolean include_start, - Boolean include_end, - ArrayOf(String) replacement, - Error *err) +static bool check_string_array(Array arr, bool disallow_nl, Error *err) { - start = convert_index(start) + !include_start; - end = convert_index(end) + include_end; - nvim_buf_set_lines(0, buffer, start, end, false, replacement, err); + for (size_t i = 0; i < arr.size; i++) { + if (arr.items[i].type != kObjectTypeString) { + api_set_error(err, + kErrorTypeValidation, + "All items in the replacement array must be strings"); + return false; + } + // Disallow newlines in the middle of the line. + if (disallow_nl) { + const String l = arr.items[i].data.string; + if (memchr(l.data, NL, l.size)) { + api_set_error(err, kErrorTypeValidation, + "String cannot contain newlines"); + return false; + } + } + } + return true; } - /// Sets (replaces) a line-range in the buffer. /// /// Indexing is zero-based, end-exclusive. Negative indices are interpreted @@ -448,6 +358,7 @@ void nvim_buf_set_lines(uint64_t channel_id, ArrayOf(String) replacement, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -472,22 +383,9 @@ void nvim_buf_set_lines(uint64_t channel_id, return; } - for (size_t i = 0; i < replacement.size; i++) { - if (replacement.items[i].type != kObjectTypeString) { - api_set_error(err, - kErrorTypeValidation, - "All items in the replacement array must be strings"); - return; - } - // Disallow newlines in the middle of the line. - if (channel_id != VIML_INTERNAL_CALL) { - const String l = replacement.items[i].data.string; - if (memchr(l.data, NL, l.size)) { - api_set_error(err, kErrorTypeValidation, - "String cannot contain newlines"); - return; - } - } + bool disallow_nl = (channel_id != VIML_INTERNAL_CALL); + if (!check_string_array(replacement, disallow_nl, err)) { + return; } size_t new_len = replacement.size; @@ -597,6 +495,250 @@ end: try_end(err); } +/// Sets (replaces) a range in the buffer +/// +/// This is recommended over nvim_buf_set_lines when only modifying parts of a +/// line, as extmarks will be preserved on non-modified parts of the touched +/// lines. +/// +/// Indexing is zero-based and end-exclusive. +/// +/// To insert text at a given index, set `start` and `end` ranges to the same +/// index. To delete a range, set `replacement` to an array containing +/// an empty string, or simply an empty array. +/// +/// Prefer nvim_buf_set_lines when adding or deleting entire lines only. +/// +/// @param channel_id +/// @param buffer Buffer handle, or 0 for current buffer +/// @param start_row First line index +/// @param start_column Last column +/// @param end_row Last line index +/// @param end_column Last column +/// @param replacement Array of lines to use as replacement +/// @param[out] err Error details, if any +void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, + Integer start_row, Integer start_col, + Integer end_row, Integer end_col, + ArrayOf(String) replacement, Error *err) + FUNC_API_SINCE(7) +{ + FIXED_TEMP_ARRAY(scratch, 1); + if (replacement.size == 0) { + scratch.items[0] = STRING_OBJ(STATIC_CSTR_AS_STRING("")); + replacement = scratch; + } + + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return; + } + + bool oob = false; + + // check range is ordered and everything! + // start_row, end_row within buffer len (except add text past the end?) + start_row = normalize_index(buf, start_row, &oob); + if (oob || start_row == buf->b_ml.ml_line_count + 1) { + api_set_error(err, kErrorTypeValidation, "start_row out of bounds"); + return; + } + + end_row = normalize_index(buf, end_row, &oob); + if (oob || end_row == buf->b_ml.ml_line_count + 1) { + api_set_error(err, kErrorTypeValidation, "end_row out of bounds"); + return; + } + + char *str_at_start = (char *)ml_get_buf(buf, start_row, false); + if (start_col < 0 || (size_t)start_col > strlen(str_at_start)) { + api_set_error(err, kErrorTypeValidation, "start_col out of bounds"); + return; + } + + char *str_at_end = (char *)ml_get_buf(buf, end_row, false); + size_t len_at_end = strlen(str_at_end); + if (end_col < 0 || (size_t)end_col > len_at_end) { + api_set_error(err, kErrorTypeValidation, "end_col out of bounds"); + return; + } + + if (start_row > end_row || (end_row == start_row && start_col > end_col)) { + api_set_error(err, kErrorTypeValidation, "start is higher than end"); + return; + } + + bool disallow_nl = (channel_id != VIML_INTERNAL_CALL); + if (!check_string_array(replacement, disallow_nl, err)) { + return; + } + + size_t new_len = replacement.size; + + bcount_t new_byte = 0; + bcount_t old_byte = 0; + + // calculate byte size of old region before it gets modified/deleted + if (start_row == end_row) { + old_byte = (bcount_t)end_col - start_col; + } else { + const char *bufline; + old_byte += (bcount_t)strlen(str_at_start) - start_col; + for (int64_t i = 1; i < end_row - start_row; i++) { + int64_t lnum = start_row + i; + + bufline = (char *)ml_get_buf(buf, lnum, false); + old_byte += (bcount_t)(strlen(bufline))+1; + } + old_byte += (bcount_t)end_col+1; + } + + String first_item = replacement.items[0].data.string; + String last_item = replacement.items[replacement.size-1].data.string; + + size_t firstlen = (size_t)start_col+first_item.size; + size_t last_part_len = strlen(str_at_end) - (size_t)end_col; + if (replacement.size == 1) { + firstlen += last_part_len; + } + char *first = xmallocz(firstlen), *last = NULL; + memcpy(first, str_at_start, (size_t)start_col); + memcpy(first+start_col, first_item.data, first_item.size); + memchrsub(first+start_col, NUL, NL, first_item.size); + if (replacement.size == 1) { + memcpy(first+start_col+first_item.size, str_at_end+end_col, last_part_len); + } else { + last = xmallocz(last_item.size+last_part_len); + memcpy(last, last_item.data, last_item.size); + memchrsub(last, NUL, NL, last_item.size); + memcpy(last+last_item.size, str_at_end+end_col, last_part_len); + } + + char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; + lines[0] = first; + new_byte += (bcount_t)(first_item.size); + for (size_t i = 1; i < new_len-1; i++) { + const String l = replacement.items[i].data.string; + + // Fill lines[i] with l's contents. Convert NULs to newlines as required by + // NL-used-for-NUL. + lines[i] = xmemdupz(l.data, l.size); + memchrsub(lines[i], NUL, NL, l.size); + new_byte += (bcount_t)(l.size)+1; + } + if (replacement.size > 1) { + lines[replacement.size-1] = last; + new_byte += (bcount_t)(last_item.size)+1; + } + + try_start(); + aco_save_T aco; + aucmd_prepbuf(&aco, (buf_T *)buf); + + if (!MODIFIABLE(buf)) { + api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'"); + goto end; + } + + // Small note about undo states: unlike set_lines, we want to save the + // undo state of one past the end_row, since end_row is inclusive. + if (u_save((linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to save undo information"); + goto end; + } + + ptrdiff_t extra = 0; // lines added to text, can be negative + size_t old_len = (size_t)(end_row-start_row+1); + + // If the size of the range is reducing (ie, new_len < old_len) we + // need to delete some old_len. We do this at the start, by + // repeatedly deleting line "start". + size_t to_delete = (new_len < old_len) ? (size_t)(old_len - new_len) : 0; + for (size_t i = 0; i < to_delete; i++) { + if (ml_delete((linenr_T)start_row, false) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to delete line"); + goto end; + } + } + + if (to_delete > 0) { + extra -= (ptrdiff_t)to_delete; + } + + // For as long as possible, replace the existing old_len with the + // new old_len. This is a more efficient operation, as it requires + // less memory allocation and freeing. + size_t to_replace = old_len < new_len ? old_len : new_len; + for (size_t i = 0; i < to_replace; i++) { + int64_t lnum = start_row + (int64_t)i; + + if (lnum >= MAXLNUM) { + api_set_error(err, kErrorTypeValidation, "Index value is too high"); + goto end; + } + + if (ml_replace((linenr_T)lnum, (char_u *)lines[i], false) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to replace line"); + goto end; + } + // Mark lines that haven't been passed to the buffer as they need + // to be freed later + lines[i] = NULL; + } + + // Now we may need to insert the remaining new old_len + for (size_t i = to_replace; i < new_len; i++) { + int64_t lnum = start_row + (int64_t)i - 1; + + if (lnum >= MAXLNUM) { + api_set_error(err, kErrorTypeValidation, "Index value is too high"); + goto end; + } + + if (ml_append((linenr_T)lnum, (char_u *)lines[i], 0, false) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to insert line"); + goto end; + } + + // Same as with replacing, but we also need to free lines + xfree(lines[i]); + lines[i] = NULL; + extra++; + } + + // Adjust marks. Invalidate any which lie in the + // changed range, and move any in the remainder of the buffer. + mark_adjust((linenr_T)start_row, + (linenr_T)end_row, + MAXLNUM, + (long)extra, + kExtmarkNOOP); + + colnr_T col_extent = (colnr_T)(end_col + - ((end_row == start_row) ? start_col : 0)); + extmark_splice(buf, (int)start_row-1, (colnr_T)start_col, + (int)(end_row-start_row), col_extent, old_byte, + (int)new_len-1, (colnr_T)last_item.size, new_byte, + kExtmarkUndo); + + + changed_lines((linenr_T)start_row, 0, (linenr_T)end_row, (long)extra, true); + + // adjust cursor like an extmark ( i e it was inside last_part_len) + if (curwin->w_cursor.lnum == end_row && curwin->w_cursor.col > end_col) { + curwin->w_cursor.col -= col_extent - (colnr_T)last_item.size; + } + fix_cursor((linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra); + +end: + for (size_t i = 0; i < new_len; i++) { + xfree(lines[i]); + } + xfree(lines); + aucmd_restbuf(&aco); + try_end(err); +} + /// Returns the byte offset of a line (0-indexed). |api-indexing| /// /// Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is one byte. @@ -787,48 +929,6 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err) dict_set_var(buf->b_vars, name, NIL, true, false, err); } -/// Sets a buffer-scoped (b:) variable -/// -/// @deprecated -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param name Variable name -/// @param value Variable value -/// @param[out] err Error details, if any -/// @return Old value or nil if there was no previous value. -/// -/// @warning It may return nil if there was no previous value -/// or if previous value was `v:null`. -Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) -{ - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!buf) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(buf->b_vars, name, value, false, true, err); -} - -/// Removes a buffer-scoped (b:) variable -/// -/// @deprecated -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param name Variable name -/// @param[out] err Error details, if any -/// @return Old value -Object buffer_del_var(Buffer buffer, String name, Error *err) -{ - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!buf) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(buf->b_vars, name, NIL, true, true, err); -} - /// Gets a buffer option value /// @@ -869,28 +969,6 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, set_option_to(channel_id, buf, SREQ_BUF, name, value, err); } -/// Gets the buffer number -/// -/// @deprecated The buffer number now is equal to the object id, -/// so there is no need to use this function. -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param[out] err Error details, if any -/// @return Buffer number -Integer nvim_buf_get_number(Buffer buffer, Error *err) - FUNC_API_SINCE(1) - FUNC_API_DEPRECATED_SINCE(2) -{ - Integer rv = 0; - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!buf) { - return rv; - } - - return buf->b_fnum; -} - /// Gets the full file name for the buffer /// /// @param buffer Buffer handle, or 0 for current buffer @@ -962,6 +1040,7 @@ Boolean nvim_buf_is_loaded(Buffer buffer) /// - unload: Unloaded only, do not delete. See |:bunload| void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) FUNC_API_SINCE(7) + FUNC_API_CHECK_TEXTLOCK { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -1017,25 +1096,6 @@ Boolean nvim_buf_is_valid(Buffer buffer) return ret; } -/// Inserts a sequence of lines to a buffer at a certain index -/// -/// @deprecated use nvim_buf_set_lines(buffer, lnum, lnum, true, lines) -/// -/// @param buffer Buffer handle -/// @param lnum Insert the lines after `lnum`. If negative, appends to -/// the end of the buffer. -/// @param lines Array of lines -/// @param[out] err Error details, if any -void buffer_insert(Buffer buffer, - Integer lnum, - ArrayOf(String) lines, - Error *err) -{ - // "lnum" will be the index of the line after inserting, - // no matter if it is negative or not - nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, err); -} - /// Return a tuple (row,col) representing the position of the named mark. /// /// Marks are (1,0)-indexed. |api-indexing| @@ -1667,27 +1727,6 @@ void nvim_buf_clear_namespace(Buffer buffer, (int)line_end-1, MAXCOL); } -/// Clears highlights and virtual text from namespace and range of lines -/// -/// @deprecated use |nvim_buf_clear_namespace()|. -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param ns_id Namespace to clear, or -1 to clear all. -/// @param line_start Start of range of lines to clear -/// @param line_end End of range of lines to clear (exclusive) or -1 to clear -/// to end of file. -/// @param[out] err Error details, if any -void nvim_buf_clear_highlight(Buffer buffer, - Integer ns_id, - Integer line_start, - Integer line_end, - Error *err) - FUNC_API_SINCE(1) -{ - nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err); -} - - /// Set the virtual text (annotation) for a buffer line. /// /// By default (and currently the only option) the text will be placed after @@ -1873,8 +1912,3 @@ static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob) index++; return index; } - -static int64_t convert_index(int64_t index) -{ - return index < 0 ? index - 1 : index; -} diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c new file mode 100644 index 0000000000..3989386bb9 --- /dev/null +++ b/src/nvim/api/deprecated.c @@ -0,0 +1,367 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <limits.h> + +#include "nvim/api/deprecated.h" +#include "nvim/api/buffer.h" +#include "nvim/api/vim.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/private/defs.h" +#include "nvim/lua/executor.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/deprecated.c.generated.h" +#endif + +/// @deprecated +/// @see nvim_exec +String nvim_command_output(String command, Error *err) + FUNC_API_SINCE(1) + FUNC_API_DEPRECATED_SINCE(7) +{ + return nvim_exec(command, true, err); +} + +/// @deprecated Use nvim_exec_lua() instead. +/// @see nvim_exec_lua +Object nvim_execute_lua(String code, Array args, Error *err) + FUNC_API_SINCE(3) + FUNC_API_DEPRECATED_SINCE(7) + FUNC_API_REMOTE_ONLY +{ + return nlua_exec(code, args, err); +} + +/// Gets the buffer number +/// +/// @deprecated The buffer number now is equal to the object id, +/// so there is no need to use this function. +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param[out] err Error details, if any +/// @return Buffer number +Integer nvim_buf_get_number(Buffer buffer, Error *err) + FUNC_API_SINCE(1) + FUNC_API_DEPRECATED_SINCE(2) +{ + Integer rv = 0; + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return rv; + } + + return buf->b_fnum; +} + +/// Clears highlights and virtual text from namespace and range of lines +/// +/// @deprecated use |nvim_buf_clear_namespace()|. +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param ns_id Namespace to clear, or -1 to clear all. +/// @param line_start Start of range of lines to clear +/// @param line_end End of range of lines to clear (exclusive) or -1 to clear +/// to end of file. +/// @param[out] err Error details, if any +void nvim_buf_clear_highlight(Buffer buffer, + Integer ns_id, + Integer line_start, + Integer line_end, + Error *err) + FUNC_API_SINCE(1) + FUNC_API_DEPRECATED_SINCE(7) +{ + nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err); +} + + +/// Inserts a sequence of lines to a buffer at a certain index +/// +/// @deprecated use nvim_buf_set_lines(buffer, lnum, lnum, true, lines) +/// +/// @param buffer Buffer handle +/// @param lnum Insert the lines after `lnum`. If negative, appends to +/// the end of the buffer. +/// @param lines Array of lines +/// @param[out] err Error details, if any +void buffer_insert(Buffer buffer, + Integer lnum, + ArrayOf(String) lines, + Error *err) +{ + // "lnum" will be the index of the line after inserting, + // no matter if it is negative or not + nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, err); +} + +/// Gets a buffer line +/// +/// @deprecated use nvim_buf_get_lines instead. +/// for positive indices (including 0) use +/// "nvim_buf_get_lines(buffer, index, index+1, true)" +/// for negative indices use +/// "nvim_buf_get_lines(buffer, index-1, index, true)" +/// +/// @param buffer Buffer handle +/// @param index Line index +/// @param[out] err Error details, if any +/// @return Line string +String buffer_get_line(Buffer buffer, Integer index, Error *err) +{ + String rv = { .size = 0 }; + + index = convert_index(index); + Array slice = nvim_buf_get_lines(0, buffer, index, index+1, true, err); + + if (!ERROR_SET(err) && slice.size) { + rv = slice.items[0].data.string; + } + + xfree(slice.items); + + return rv; +} + +/// Sets a buffer line +/// +/// @deprecated use nvim_buf_set_lines instead. +/// for positive indices use +/// "nvim_buf_set_lines(buffer, index, index+1, true, [line])" +/// for negative indices use +/// "nvim_buf_set_lines(buffer, index-1, index, true, [line])" +/// +/// @param buffer Buffer handle +/// @param index Line index +/// @param line Contents of the new line +/// @param[out] err Error details, if any +void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) +{ + Object l = STRING_OBJ(line); + Array array = { .items = &l, .size = 1 }; + index = convert_index(index); + nvim_buf_set_lines(0, buffer, index, index+1, true, array, err); +} + +/// Deletes a buffer line +/// +/// @deprecated use nvim_buf_set_lines instead. +/// for positive indices use +/// "nvim_buf_set_lines(buffer, index, index+1, true, [])" +/// for negative indices use +/// "nvim_buf_set_lines(buffer, index-1, index, true, [])" +/// @param buffer buffer handle +/// @param index line index +/// @param[out] err Error details, if any +void buffer_del_line(Buffer buffer, Integer index, Error *err) +{ + Array array = ARRAY_DICT_INIT; + index = convert_index(index); + nvim_buf_set_lines(0, buffer, index, index+1, true, array, err); +} + +/// Retrieves a line range from the buffer +/// +/// @deprecated use nvim_buf_get_lines(buffer, newstart, newend, false) +/// where newstart = start + int(not include_start) - int(start < 0) +/// newend = end + int(include_end) - int(end < 0) +/// int(bool) = 1 if bool is true else 0 +/// @param buffer Buffer handle +/// @param start First line index +/// @param end Last line index +/// @param include_start True if the slice includes the `start` parameter +/// @param include_end True if the slice includes the `end` parameter +/// @param[out] err Error details, if any +/// @return Array of lines +ArrayOf(String) buffer_get_line_slice(Buffer buffer, + Integer start, + Integer end, + Boolean include_start, + Boolean include_end, + Error *err) +{ + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + return nvim_buf_get_lines(0, buffer, start , end, false, err); +} + +/// Replaces a line range on the buffer +/// +/// @deprecated use nvim_buf_set_lines(buffer, newstart, newend, false, lines) +/// where newstart = start + int(not include_start) + int(start < 0) +/// newend = end + int(include_end) + int(end < 0) +/// int(bool) = 1 if bool is true else 0 +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param start First line index +/// @param end Last line index +/// @param include_start True if the slice includes the `start` parameter +/// @param include_end True if the slice includes the `end` parameter +/// @param replacement Array of lines to use as replacement (0-length +// array will delete the line range) +/// @param[out] err Error details, if any +void buffer_set_line_slice(Buffer buffer, + Integer start, + Integer end, + Boolean include_start, + Boolean include_end, + ArrayOf(String) replacement, + Error *err) +{ + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + nvim_buf_set_lines(0, buffer, start, end, false, replacement, err); +} + + +/// Sets a buffer-scoped (b:) variable +/// +/// @deprecated +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any +/// @return Old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return NIL; + } + + return dict_set_var(buf->b_vars, name, value, false, true, err); +} + +/// Removes a buffer-scoped (b:) variable +/// +/// @deprecated +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Old value +Object buffer_del_var(Buffer buffer, String name, Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return NIL; + } + + return dict_set_var(buf->b_vars, name, NIL, true, true, err); +} + +/// Sets a window-scoped (w:) variable +/// +/// @deprecated +/// +/// @param window Window handle, or 0 for current window +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any +/// @return Old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object window_set_var(Window window, String name, Object value, Error *err) +{ + win_T *win = find_window_by_handle(window, err); + + if (!win) { + return NIL; + } + + return dict_set_var(win->w_vars, name, value, false, true, err); +} + +/// Removes a window-scoped (w:) variable +/// +/// @deprecated +/// +/// @param window Window handle, or 0 for current window +/// @param name variable name +/// @param[out] err Error details, if any +/// @return Old value +Object window_del_var(Window window, String name, Error *err) +{ + win_T *win = find_window_by_handle(window, err); + + if (!win) { + return NIL; + } + + return dict_set_var(win->w_vars, name, NIL, true, true, err); +} + +/// Sets a tab-scoped (t:) variable +/// +/// @deprecated +/// +/// @param tabpage Tabpage handle, or 0 for current tabpage +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any +/// @return Old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) +{ + tabpage_T *tab = find_tab_by_handle(tabpage, err); + + if (!tab) { + return NIL; + } + + return dict_set_var(tab->tp_vars, name, value, false, true, err); +} + +/// Removes a tab-scoped (t:) variable +/// +/// @deprecated +/// +/// @param tabpage Tabpage handle, or 0 for current tabpage +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Old value +Object tabpage_del_var(Tabpage tabpage, String name, Error *err) +{ + tabpage_T *tab = find_tab_by_handle(tabpage, err); + + if (!tab) { + return NIL; + } + + return dict_set_var(tab->tp_vars, name, NIL, true, true, err); +} + +/// @deprecated +/// @see nvim_set_var +/// @warning May return nil if there was no previous value +/// OR if previous value was `v:null`. +/// @return Old value or nil if there was no previous value. +Object vim_set_var(String name, Object value, Error *err) +{ + return dict_set_var(&globvardict, name, value, false, true, err); +} + +/// @deprecated +/// @see nvim_del_var +Object vim_del_var(String name, Error *err) +{ + return dict_set_var(&globvardict, name, NIL, true, true, err); +} + +static int64_t convert_index(int64_t index) +{ + return index < 0 ? index - 1 : index; +} diff --git a/src/nvim/api/deprecated.h b/src/nvim/api/deprecated.h new file mode 100644 index 0000000000..79095167e1 --- /dev/null +++ b/src/nvim/api/deprecated.h @@ -0,0 +1,11 @@ +#ifndef NVIM_API_DEPRECATED_H +#define NVIM_API_DEPRECATED_H + +#include <stdint.h> + +#include "nvim/api/private/defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/deprecated.h.generated.h" +#endif +#endif // NVIM_API_DEPRECATED_H diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index 2975df3c68..eae4581f42 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -19,6 +19,7 @@ #include "nvim/api/ui.h" #include "nvim/api/vim.h" #include "nvim/api/window.h" +#include "nvim/api/deprecated.h" static Map(String, MsgpackRpcRequestHandler) *methods = NULL; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2c99d3426c..8f224e8c78 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -820,6 +820,10 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, } buf_T *target_buf = find_buffer_by_handle(buffer, err); + if (!target_buf) { + return; + } + MapArguments parsed_args; memset(&parsed_args, 0, sizeof(parsed_args)); if (parse_keymap_opts(opts, &parsed_args, err)) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 271fd5b485..055abb797f 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -16,6 +16,7 @@ #define BOOLEAN_OBJ(b) ((Object) { \ .type = kObjectTypeBoolean, \ .data.boolean = b }) +#define BOOL(b) BOOLEAN_OBJ(b) #define INTEGER_OBJ(i) ((Object) { \ .type = kObjectTypeInteger, \ @@ -29,6 +30,8 @@ .type = kObjectTypeString, \ .data.string = s }) +#define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) + #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ .data.integer = s }) @@ -59,6 +62,8 @@ #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) +#define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition)); + #define ADD(array, item) \ kv_push(array, item) diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index dd17bc03e5..5f727dbc38 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -97,48 +97,6 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) dict_set_var(tab->tp_vars, name, NIL, true, false, err); } -/// Sets a tab-scoped (t:) variable -/// -/// @deprecated -/// -/// @param tabpage Tabpage handle, or 0 for current tabpage -/// @param name Variable name -/// @param value Variable value -/// @param[out] err Error details, if any -/// @return Old value or nil if there was no previous value. -/// -/// @warning It may return nil if there was no previous value -/// or if previous value was `v:null`. -Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) -{ - tabpage_T *tab = find_tab_by_handle(tabpage, err); - - if (!tab) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(tab->tp_vars, name, value, false, true, err); -} - -/// Removes a tab-scoped (t:) variable -/// -/// @deprecated -/// -/// @param tabpage Tabpage handle, or 0 for current tabpage -/// @param name Variable name -/// @param[out] err Error details, if any -/// @return Old value -Object tabpage_del_var(Tabpage tabpage, String name, Error *err) -{ - tabpage_T *tab = find_tab_by_handle(tabpage, err); - - if (!tab) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(tab->tp_vars, name, NIL, true, true, err); -} - /// Gets the current window in a tabpage /// /// @param tabpage Tabpage handle, or 0 for current tabpage diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 77002697fe..1e972e01be 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -15,6 +15,7 @@ #include "nvim/api/private/dispatch.h" #include "nvim/api/buffer.h" #include "nvim/api/window.h" +#include "nvim/api/deprecated.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/lua/executor.h" @@ -479,15 +480,6 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, return cstr_as_string(ptr); } -/// @deprecated -/// @see nvim_exec -String nvim_command_output(String command, Error *err) - FUNC_API_SINCE(1) - FUNC_API_DEPRECATED_SINCE(7) -{ - return nvim_exec(command, true, err); -} - /// Evaluates a VimL |expression|. /// Dictionaries and Lists are recursively expanded. /// @@ -534,16 +526,6 @@ Object nvim_eval(String expr, Error *err) return rv; } -/// @deprecated Use nvim_exec_lua() instead. -/// @see nvim_exec_lua -Object nvim_execute_lua(String code, Array args, Error *err) - FUNC_API_SINCE(3) - FUNC_API_DEPRECATED_SINCE(7) - FUNC_API_REMOTE_ONLY -{ - return nlua_exec(code, args, err); -} - /// Execute Lua code. Parameters (if any) are available as `...` inside the /// chunk. The chunk can return a value. /// @@ -875,6 +857,7 @@ String nvim_get_current_line(Error *err) /// @param[out] err Error details, if any void nvim_set_current_line(String line, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err); } @@ -884,6 +867,7 @@ void nvim_set_current_line(String line, Error *err) /// @param[out] err Error details, if any void nvim_del_current_line(Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); } @@ -920,23 +904,6 @@ void nvim_del_var(String name, Error *err) dict_set_var(&globvardict, name, NIL, true, false, err); } -/// @deprecated -/// @see nvim_set_var -/// @warning May return nil if there was no previous value -/// OR if previous value was `v:null`. -/// @return Old value or nil if there was no previous value. -Object vim_set_var(String name, Object value, Error *err) -{ - return dict_set_var(&globvardict, name, value, false, true, err); -} - -/// @deprecated -/// @see nvim_del_var -Object vim_del_var(String name, Error *err) -{ - return dict_set_var(&globvardict, name, NIL, true, true, err); -} - /// Gets a v: variable. /// /// @param name Variable name @@ -970,6 +937,47 @@ Object nvim_get_option(String name, Error *err) return get_option_from(NULL, SREQ_GLOBAL, name, err); } +/// Gets the option information for all options. +/// +/// The dictionary has the full option names as keys and option metadata +/// dictionaries as detailed at |nvim_get_option_info|. +/// +/// @return dictionary of all options +Dictionary nvim_get_all_options_info(Error *err) + FUNC_API_SINCE(7) +{ + return get_all_vimoptions(); +} + +/// Gets the option information for one option +/// +/// Resulting dictionary has keys: +/// - name: Name of the option (like 'filetype') +/// - shortname: Shortened name of the option (like 'ft') +/// - type: type of option ("string", "integer" or "boolean") +/// - default: The default value for the option +/// - was_set: Whether the option was set. +/// +/// - last_set_sid: Last set script id (if any) +/// - last_set_linenr: line number where option was set +/// - last_set_chan: Channel where option was set (0 for local) +/// +/// - scope: one of "global", "win", or "buf" +/// - global_local: whether win or buf option has a global value +/// +/// - commalist: List of comma separated values +/// - flaglist: List of single char flags +/// +/// +/// @param name Option name +/// @param[out] err Error details, if any +/// @return Option Information +Dictionary nvim_get_option_info(String name, Error *err) + FUNC_API_SINCE(7) +{ + return get_vimoption(name, err); +} + /// Sets an option value. /// /// @param channel_id @@ -1054,6 +1062,7 @@ Buffer nvim_get_current_buf(void) /// @param[out] err Error details, if any void nvim_set_current_buf(Buffer buffer, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -1108,6 +1117,7 @@ Window nvim_get_current_win(void) /// @param[out] err Error details, if any void nvim_set_current_win(Window window, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { win_T *win = find_window_by_handle(window, err); @@ -1257,6 +1267,7 @@ fail: Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config, Error *err) FUNC_API_SINCE(6) + FUNC_API_CHECK_TEXTLOCK { FloatConfig fconfig = FLOAT_CONFIG_INIT; if (!parse_float_config(config, &fconfig, false, err)) { @@ -1321,6 +1332,7 @@ Tabpage nvim_get_current_tabpage(void) /// @param[out] err Error details, if any void nvim_set_current_tabpage(Tabpage tabpage, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { tabpage_T *tp = find_tab_by_handle(tabpage, err); @@ -1405,6 +1417,7 @@ Dictionary nvim_get_namespaces(void) /// - false: Client must cancel the paste. Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) FUNC_API_SINCE(6) + FUNC_API_CHECK_TEXTLOCK { static bool draining = false; bool cancel = false; @@ -1477,6 +1490,7 @@ theend: void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Error *err) FUNC_API_SINCE(6) + FUNC_API_CHECK_TEXTLOCK { yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1); if (!prepare_yankreg_from_object(reg, type, lines.size)) { diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index f09a03f592..f4af1632ec 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -44,6 +44,7 @@ Buffer nvim_win_get_buf(Window window, Error *err) /// @param[out] err Error details, if any void nvim_win_set_buf(Window window, Buffer buffer, Error *err) FUNC_API_SINCE(5) + FUNC_API_CHECK_TEXTLOCK { win_T *win = find_window_by_handle(window, err), *save_curwin = curwin; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -288,48 +289,6 @@ void nvim_win_del_var(Window window, String name, Error *err) dict_set_var(win->w_vars, name, NIL, true, false, err); } -/// Sets a window-scoped (w:) variable -/// -/// @deprecated -/// -/// @param window Window handle, or 0 for current window -/// @param name Variable name -/// @param value Variable value -/// @param[out] err Error details, if any -/// @return Old value or nil if there was no previous value. -/// -/// @warning It may return nil if there was no previous value -/// or if previous value was `v:null`. -Object window_set_var(Window window, String name, Object value, Error *err) -{ - win_T *win = find_window_by_handle(window, err); - - if (!win) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(win->w_vars, name, value, false, true, err); -} - -/// Removes a window-scoped (w:) variable -/// -/// @deprecated -/// -/// @param window Window handle, or 0 for current window -/// @param name variable name -/// @param[out] err Error details, if any -/// @return Old value -Object window_del_var(Window window, String name, Error *err) -{ - win_T *win = find_window_by_handle(window, err); - - if (!win) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(win->w_vars, name, NIL, true, true, err); -} - /// Gets a window option value /// /// @param window Window handle, or 0 for current window @@ -542,6 +501,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) /// @param[out] err Error details, if any void nvim_win_close(Window window, Boolean force, Error *err) FUNC_API_SINCE(6) + FUNC_API_CHECK_TEXTLOCK { win_T *win = find_window_by_handle(window, err); if (!win) { 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/autocmd.c b/src/nvim/autocmd.c index 53b11c250e..42224d0a4f 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1106,9 +1106,9 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) win = curwin; } - aco->save_curwin = curwin; - aco->save_prevwin = prevwin; + aco->save_curwin_handle = curwin->handle; aco->save_curbuf = curbuf; + aco->save_prevwin_handle = prevwin == NULL ? 0 : prevwin->handle; if (win != NULL) { // There is a window for "buf" in the current tab page, make it the // curwin. This is preferred, it has the least side effects (esp. if @@ -1148,7 +1148,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) curwin = aucmd_win; } curbuf = buf; - aco->new_curwin = curwin; + aco->new_curwin_handle = curwin->handle; set_bufref(&aco->new_curbuf, curbuf); } @@ -1194,14 +1194,14 @@ void aucmd_restbuf(aco_save_T *aco) unblock_autocmds(); - if (win_valid(aco->save_curwin)) { - curwin = aco->save_curwin; + win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle); + if (save_curwin != NULL) { + curwin = save_curwin; } else { // Hmm, original window disappeared. Just use the first one. curwin = firstwin; } - prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin - : firstwin; // window disappeared? + prevwin = win_find_by_handle(aco->save_prevwin_handle); vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab curbuf = curwin->w_buffer; @@ -1216,11 +1216,14 @@ void aucmd_restbuf(aco_save_T *aco) curwin->w_topfill = 0; } } else { - // restore curwin - if (win_valid(aco->save_curwin)) { + // Restore curwin. Use the window ID, a window may have been closed + // and the memory re-used for another one. + win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle); + if (save_curwin != NULL) { // Restore the buffer which was previously edited by curwin, if it was // changed, we are still the same window and the buffer is valid. - if (curwin == aco->new_curwin && curbuf != aco->new_curbuf.br_buf + if (curwin->handle == aco->new_curwin_handle + && curbuf != aco->new_curbuf.br_buf && bufref_valid(&aco->new_curbuf) && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL) { if (curwin->w_s == &curbuf->b_s) { @@ -1232,10 +1235,9 @@ void aucmd_restbuf(aco_save_T *aco) curbuf->b_nwindows++; } - curwin = aco->save_curwin; - prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin - : firstwin; // window disappeared? + curwin = save_curwin; curbuf = curwin->w_buffer; + prevwin = win_find_by_handle(aco->save_prevwin_handle); // In case the autocommand moves the cursor to a position that does not // exist in curbuf check_cursor(); @@ -1717,7 +1719,8 @@ void unblock_autocmds(void) } } -static inline bool is_autocmd_blocked(void) +bool is_autocmd_blocked(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return autocmd_blocked != 0; } diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index af1eeb0fc4..1c0f88f08f 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -7,13 +7,13 @@ // Struct to save values in before executing autocommands for a buffer that is // not the current buffer. typedef struct { - buf_T *save_curbuf; ///< saved curbuf - int use_aucmd_win; ///< using aucmd_win - win_T *save_curwin; ///< saved curwin - win_T *save_prevwin; ///< saved prevwin - win_T *new_curwin; ///< new curwin - bufref_T new_curbuf; ///< new curbuf - char_u *globaldir; ///< saved value of globaldir + buf_T *save_curbuf; ///< saved curbuf + bool use_aucmd_win; ///< using aucmd_win + handle_T save_curwin_handle; ///< ID of saved curwin + handle_T new_curwin_handle; ///< ID of new curwin + handle_T save_prevwin_handle; ///< ID of saved prevwin + bufref_T new_curbuf; ///< new curbuf + char_u *globaldir; ///< saved value of globaldir } aco_save_T; typedef struct AutoCmd { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 839d61cd2e..0134ee838d 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -602,8 +602,12 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) * Remove the buffer from the list. */ if (wipe_buf) { - xfree(buf->b_ffname); - xfree(buf->b_sfname); + if (buf->b_sfname != buf->b_ffname) { + XFREE_CLEAR(buf->b_sfname); + } else { + buf->b_sfname = NULL; + } + XFREE_CLEAR(buf->b_ffname); if (buf->b_prev == NULL) { firstbuf = buf->b_next; } else { @@ -1583,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; @@ -1693,15 +1697,18 @@ static inline void buf_init_changedtick(buf_T *const buf) /// if the buffer already exists. /// This is the ONLY way to create a new buffer. /// -/// @param ffname full path of fname or relative -/// @param sfname short fname or NULL +/// @param ffname_arg full path of fname or relative +/// @param sfname_arg short fname or NULL /// @param lnum preferred cursor line /// @param flags BLN_ defines /// @param bufnr /// /// @return pointer to the buffer -buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) +buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, + int flags) { + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; buf_T *buf; fname_expand(curbuf, &ffname, &sfname); // will allocate ffname @@ -1787,8 +1794,12 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) buf->b_wininfo = xcalloc(1, sizeof(wininfo_T)); if (ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) { + if (buf->b_sfname != buf->b_ffname) { + XFREE_CLEAR(buf->b_sfname); + } else { + buf->b_sfname = NULL; + } XFREE_CLEAR(buf->b_ffname); - XFREE_CLEAR(buf->b_sfname); if (buf != curbuf) { free_buffer(buf); } @@ -2148,10 +2159,11 @@ static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, int buflist_findpat( const char_u *pattern, const char_u *pattern_end, // pointer to first char after pattern - int unlisted, // find unlisted buffers - int diffmode, // find diff-mode buffers only - int curtab_only // find buffers in current tab only + bool unlisted, // find unlisted buffers + bool diffmode, // find diff-mode buffers only + bool curtab_only // find buffers in current tab only ) + FUNC_ATTR_NONNULL_ARG(1) { int match = -1; int find_listed; @@ -2778,28 +2790,32 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum) return OK; } -/* - * Set the file name for "buf"' to 'ffname', short file name to 'sfname'. - * The file name with the full path is also remembered, for when :cd is used. - * Returns FAIL for failure (file name already in use by other buffer) - * OK otherwise. - */ -int -setfname( +// Set the file name for "buf" to "ffname_arg", short file name to +// "sfname_arg". +// The file name with the full path is also remembered, for when :cd is used. +// Returns FAIL for failure (file name already in use by other buffer) +// OK otherwise. +int setfname( buf_T *buf, - char_u *ffname, - char_u *sfname, + char_u *ffname_arg, + char_u *sfname_arg, bool message // give message when buffer already exists ) { + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; buf_T *obuf = NULL; FileID file_id; bool file_id_valid = false; if (ffname == NULL || *ffname == NUL) { // Removing the name. + if (buf->b_sfname != buf->b_ffname) { + XFREE_CLEAR(buf->b_sfname); + } else { + buf->b_sfname = NULL; + } XFREE_CLEAR(buf->b_ffname); - XFREE_CLEAR(buf->b_sfname); } else { fname_expand(buf, &ffname, &sfname); // will allocate ffname if (ffname == NULL) { // out of memory @@ -2830,8 +2846,10 @@ setfname( #ifdef USE_FNAME_CASE path_fix_case(sfname); // set correct case for short file name #endif + if (buf->b_sfname != buf->b_ffname) { + xfree(buf->b_sfname); + } xfree(buf->b_ffname); - xfree(buf->b_sfname); buf->b_ffname = ffname; buf->b_sfname = sfname; } @@ -2857,7 +2875,9 @@ void buf_set_name(int fnum, char_u *name) buf = buflist_findnr(fnum); if (buf != NULL) { - xfree(buf->b_sfname); + if (buf->b_sfname != buf->b_ffname) { + xfree(buf->b_sfname); + } xfree(buf->b_ffname); buf->b_ffname = vim_strsave(name); buf->b_sfname = NULL; @@ -4633,16 +4653,18 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file) return true; } -/* - * Make "ffname" a full file name, set "sfname" to "ffname" if not NULL. - * "ffname" becomes a pointer to allocated memory (or NULL). - */ +// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL. +// "*ffname" becomes a pointer to allocated memory (or NULL). +// When resolving a link both "*sfname" and "*ffname" will point to the same +// allocated memory. +// The "*ffname" and "*sfname" pointer values on call will not be freed. +// Note that the resulting "*ffname" pointer should be considered not allocaed. void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) { - if (*ffname == NULL) { // if no file name given, nothing to do + if (*ffname == NULL) { // no file name given, nothing to do return; } - if (*sfname == NULL) { // if no short file name given, use ffname + if (*sfname == NULL) { // no short file name given, use ffname *sfname = *ffname; } *ffname = (char_u *)fix_fname((char *)(*ffname)); // expand to full path @@ -4685,7 +4707,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 @@ -4694,6 +4715,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 @@ -4741,6 +4763,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 @@ -4846,14 +4869,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; } @@ -4873,6 +4897,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 @@ -4898,6 +4926,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++; } @@ -5449,7 +5480,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/buffer_defs.h b/src/nvim/buffer_defs.h index dba02a67e8..cc09b7e989 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -491,9 +491,10 @@ typedef struct { LuaRef on_changedtick; LuaRef on_detach; bool utf_sizes; + bool preview; } BufUpdateCallbacks; #define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, false } + LUA_NOREF, false, false } EXTERN int curbuf_splice_pending INIT(= 0); @@ -532,9 +533,11 @@ struct file_buffer { // b_fname is the same as b_sfname, unless ":cd" has been done, // then it is the same as b_ffname (NULL for no name). // - char_u *b_ffname; // full path file name - char_u *b_sfname; // short file name - char_u *b_fname; // current file name + char_u *b_ffname; // full path file name, allocated + char_u *b_sfname; // short file name, allocated, may be equal to + // b_ffname + char_u *b_fname; // current file name, points to b_ffname or + // b_sfname bool file_id_valid; FileID file_id; diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index fc671ad9e2..68e123896b 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -237,7 +237,7 @@ void buf_updates_send_changes(buf_T *buf, for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; - if (cb.on_lines != LUA_NOREF) { + if (cb.on_lines != LUA_NOREF && (cb.preview || !(State & CMDPREVIEW))) { Array args = ARRAY_DICT_INIT; Object items[8]; args.size = 6; // may be increased to 8 below @@ -298,7 +298,7 @@ void buf_updates_send_splice( for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; - if (cb.on_bytes != LUA_NOREF) { + if (cb.on_bytes != LUA_NOREF && (cb.preview || !(State & CMDPREVIEW))) { FIXED_TEMP_ARRAY(args, 11); // the first argument is always the buffer handle diff --git a/src/nvim/charset.c b/src/nvim/charset.c index fb158f377a..3e52b3e3ce 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -45,8 +45,6 @@ static bool chartab_initialized = false; (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) #define GET_CHARTAB_TAB(chartab, c) \ ((chartab)[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) -#define GET_CHARTAB(buf, c) \ - GET_CHARTAB_TAB((buf)->b_chartab, c) // Table used below, see init_chartab() for an explanation static char_u g_chartab[256]; 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 5d44c3274e..b2abb06075 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -196,8 +196,8 @@ static int ctrl_x_mode = CTRL_X_NORMAL; static int compl_matches = 0; static char_u *compl_pattern = NULL; -static int compl_direction = FORWARD; -static int compl_shows_dir = FORWARD; +static Direction compl_direction = FORWARD; +static Direction compl_shows_dir = FORWARD; static int compl_pending = 0; // > 1 for postponed CTRL-N static pos_T compl_startpos; static colnr_T compl_col = 0; /* column where the text starts @@ -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; @@ -2156,7 +2156,7 @@ static bool ins_compl_accept_char(int c) /// /// @param[in] cont_s_ipos next ^X<> will set initial_pos int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, - int dir, bool cont_s_ipos) + Direction dir, bool cont_s_ipos) FUNC_ATTR_NONNULL_ARG(1) { char_u *str = str_arg; @@ -2308,7 +2308,7 @@ static int ins_compl_add(char_u *const str, int len, FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; - int dir = (cdir == kDirectionNotSet ? compl_direction : cdir); + const Direction dir = (cdir == kDirectionNotSet ? compl_direction : cdir); int flags = flags_arg; os_breakcheck(); @@ -2511,7 +2511,7 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) FUNC_ATTR_NONNULL_ALL { int add_r = OK; - int dir = compl_direction; + Direction dir = compl_direction; for (int i = 0; i < num_matches && add_r != FAIL; i++) { if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir, @@ -2864,7 +2864,7 @@ ins_compl_dictionaries ( char_u **files; int count; int save_p_scs; - int dir = compl_direction; + Direction dir = compl_direction; if (*dict == NUL) { /* When 'dictionary' is empty and spell checking is enabled use @@ -2945,7 +2945,10 @@ theend: xfree(buf); } -static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir) +static void ins_compl_files(int count, char_u **files, int thesaurus, + int flags, regmatch_T *regmatch, char_u *buf, + Direction *dir) + FUNC_ATTR_NONNULL_ARG(2, 7) { char_u *ptr; int i; @@ -2955,6 +2958,7 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, for (i = 0; i < count && !got_int && !compl_interrupted; i++) { fp = os_fopen((char *)files[i], "r"); // open dictionary file if (flags != DICT_EXACT) { + msg_hist_off = true; // reset in msg_trunc_attr() vim_snprintf((char *)IObuff, IOSIZE, _("Scanning dictionary: %s"), (char *)files[i]); (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); @@ -3137,6 +3141,56 @@ bool ins_compl_active(void) return compl_started; } +static void ins_compl_update_sequence_numbers(void) +{ + int number = 0; + compl_T *match; + + if (compl_direction == FORWARD) { + // search backwards for the first valid (!= -1) number. + // This should normally succeed already at the first loop + // cycle, so it's fast! + for (match = compl_curr_match->cp_prev; + match != NULL && match != compl_first_match; + match = match->cp_prev) { + if (match->cp_number != -1) { + number = match->cp_number; + break; + } + } + if (match != NULL) { + // go up and assign all numbers which are not assigned yet + for (match = match->cp_next; + match != NULL && match->cp_number == -1; + match = match->cp_next) { + match->cp_number = ++number; + } + } + } else { // BACKWARD + assert(compl_direction == BACKWARD); + // search forwards (upwards) for the first valid (!= -1) + // number. This should normally succeed already at the + // first loop cycle, so it's fast! + for (match = compl_curr_match->cp_next; + match != NULL && match != compl_first_match; + match = match->cp_next) { + if (match->cp_number != -1) { + number = match->cp_number; + break; + } + } + if (match != NULL) { + // go down and assign all numbers which are not + // assigned yet + for (match = match->cp_prev; + match && match->cp_number == -1; + match = match->cp_prev) { + match->cp_number = ++number; + } + } + } +} + // Get complete information void get_complete_info(list_T *what_list, dict_T *retdict) { @@ -3214,6 +3268,9 @@ void get_complete_info(list_T *what_list, dict_T *retdict) } if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { + if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) { + ins_compl_update_sequence_numbers(); + } ret = tv_dict_add_nr(retdict, S_LEN("selected"), (compl_curr_match != NULL) ? compl_curr_match->cp_number - 1 : -1); @@ -3366,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(); } @@ -3659,7 +3716,7 @@ static bool ins_compl_prep(int c) retval = true; } - auto_format(FALSE, TRUE); + auto_format(false, true); // Trigger the CompleteDonePre event to give scripts a chance to // act upon the completion before clearing the info, and restore @@ -3753,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); @@ -3865,7 +3922,7 @@ theend: */ static void ins_compl_add_list(list_T *const list) { - int dir = compl_direction; + Direction dir = compl_direction; // Go through the List with matches and add each of them. TV_LIST_ITER(list, li, { @@ -4057,6 +4114,7 @@ static int ins_compl_get_exp(pos_T *ini) dict = ins_buf->b_fname; dict_f = DICT_EXACT; } + msg_hist_off = true; // reset in msg_trunc_attr() vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), ins_buf->b_fname == NULL ? buf_spname(ins_buf) @@ -4078,11 +4136,12 @@ static int ins_compl_get_exp(pos_T *ini) dict = e_cpt; dict_f = DICT_FIRST; } - } else if (*e_cpt == 'i') + } else if (*e_cpt == 'i') { type = CTRL_X_PATH_PATTERNS; - else if (*e_cpt == 'd') + } else if (*e_cpt == 'd') { type = CTRL_X_PATH_DEFINES; - else if (*e_cpt == ']' || *e_cpt == 't') { + } else if (*e_cpt == ']' || *e_cpt == 't') { + msg_hist_off = true; // reset in msg_trunc_attr() type = CTRL_X_TAGS; vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags.")); (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); @@ -4665,9 +4724,11 @@ ins_compl_next ( MB_PTR_ADV(s); } } + msg_hist_off = true; vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, s > compl_shown_match->cp_fname ? "<" : "", s); msg(IObuff); + msg_hist_off = false; redraw_cmdline = false; // don't overwrite! } } @@ -5242,52 +5303,11 @@ static int ins_complete(int c, bool enable_pum) } else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) { edit_submode_extra = (char_u *)_("The only match"); edit_submode_highl = HLF_COUNT; + compl_curr_match->cp_number = 1; } else { // Update completion sequence number when needed. if (compl_curr_match->cp_number == -1) { - int number = 0; - compl_T *match; - - if (compl_direction == FORWARD) { - /* search backwards for the first valid (!= -1) number. - * This should normally succeed already at the first loop - * cycle, so it's fast! */ - for (match = compl_curr_match->cp_prev; match != NULL - && match != compl_first_match; - match = match->cp_prev) - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - if (match != NULL) - /* go up and assign all numbers which are not assigned - * yet */ - for (match = match->cp_next; - match != NULL && match->cp_number == -1; - match = match->cp_next) - match->cp_number = ++number; - } else { // BACKWARD - // search forwards (upwards) for the first valid (!= -1) - // number. This should normally succeed already at the - // first loop cycle, so it's fast! - for (match = compl_curr_match->cp_next; - match != NULL && match != compl_first_match; - match = match->cp_next) { - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - } - if (match != NULL) { - // go down and assign all numbers which are not - // assigned yet - for (match = match->cp_prev; - match && match->cp_number == -1; - match = match->cp_prev) { - match->cp_number = ++number; - } - } - } + ins_compl_update_sequence_numbers(); } /* The match should always have a sequence number now, this is @@ -5307,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); + } } } } @@ -5318,9 +5339,11 @@ static int ins_complete(int c, bool enable_pum) if (!shortmess(SHM_COMPLETIONMENU)) { if (edit_submode_extra != NULL) { if (!p_smd) { + msg_hist_off = true; msg_attr((const char *)edit_submode_extra, (edit_submode_highl < HLF_COUNT ? HL_ATTR(edit_submode_highl) : 0)); + msg_hist_off = false; } } else { msg_clr_cmdline(); // necessary for "noshowmode" @@ -6136,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); } } @@ -6481,7 +6504,7 @@ stop_insert ( curwin->w_cursor = tpos; } - auto_format(TRUE, FALSE); + auto_format(true, false); if (ascii_iswhite(cc)) { if (gchar_cursor() != NUL) @@ -6785,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; @@ -6836,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; @@ -8259,8 +8282,9 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) } } while (revins_on || (curwin->w_cursor.col > mincol - && (curwin->w_cursor.lnum != Insstart_orig.lnum - || curwin->w_cursor.col != Insstart_orig.col))); + && (can_bs(BS_NOSTOP) + || (curwin->w_cursor.lnum != Insstart_orig.lnum + || curwin->w_cursor.col != Insstart_orig.col)))); } did_backspace = true; } @@ -8306,9 +8330,6 @@ static void ins_mouse(int c) pos_T tpos; win_T *old_curwin = curwin; - if (!mouse_has(MOUSE_INSERT)) - return; - undisplay_dollar(); tpos = curwin->w_cursor; if (do_mouse(NULL, c, BACKWARD, 1, 0)) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fa037b59d7..8a1556320c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7,6 +7,8 @@ #include <math.h> +#include "auto/config.h" + #ifdef HAVE_LOCALE_H # include <locale.h> #endif @@ -74,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. @@ -2554,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; @@ -2636,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; } @@ -5402,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; @@ -5931,6 +5948,19 @@ int assert_exception(typval_T *argvars) return 0; } +static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, + const char *cmd) + FUNC_ATTR_NONNULL_ALL +{ + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { + char *const tofree = encode_tv2echo(&argvars[2], NULL); + ga_concat(gap, (char_u *)tofree); + xfree(tofree); + } else { + ga_concat(gap, (char_u *)cmd); + } +} + int assert_fails(typval_T *argvars) FUNC_ATTR_NONNULL_ALL { @@ -5949,14 +5979,7 @@ int assert_fails(typval_T *argvars) if (!called_emsg) { prepare_assert_error(&ga); ga_concat(&ga, (const char_u *)"command did not fail: "); - if (argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) { - char *const tofree = encode_tv2echo(&argvars[2], NULL); - ga_concat(&ga, (char_u *)tofree); - xfree(tofree); - } else { - ga_concat(&ga, (const char_u *)cmd); - } + assert_append_cmd_or_arg(&ga, argvars, cmd); assert_error(&ga); ga_clear(&ga); ret = 1; @@ -5969,6 +5992,8 @@ int assert_fails(typval_T *argvars) prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER); + ga_concat(&ga, (char_u *)": "); + assert_append_cmd_or_arg(&ga, argvars, cmd); assert_error(&ga); ga_clear(&ga); ret = 1; @@ -7068,7 +7093,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) { @@ -7780,13 +7805,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; @@ -10244,9 +10269,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; @@ -10263,13 +10285,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.lua b/src/nvim/eval.lua index 9f1994e299..952fa35b83 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -150,6 +150,7 @@ return { getjumplist={args={0, 2}}, getline={args={1, 2}}, getloclist={args={1, 2}}, + getmarklist={args={0, 1}}, getmatches={args={0, 1}}, getpid={}, getpos={args=1}, @@ -392,6 +393,7 @@ return { win_screenpos={args=1}, winbufnr={args=1}, wincol={}, + windowsversion={}, winheight={args=1}, winlayout={args={0, 1}}, winline={}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4df935469a..8235d74cbb 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -95,7 +95,6 @@ PRAGMA_DIAG_POP static char *e_listarg = N_("E686: Argument of %s must be a List"); -static char *e_stringreq = N_("E928: String required"); static char *e_invalwindow = N_("E957: Invalid window number"); /// Dummy va_list for passing to vim_snprintf @@ -727,7 +726,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only) p_cpo = (char_u *)""; buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name), - TRUE, FALSE, curtab_only)); + true, false, curtab_only)); p_magic = save_magic; p_cpo = save_cpo; @@ -1877,10 +1876,12 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const char *name = tv_get_string(&argvars[0]); + if (tv_check_for_string(&argvars[0]) == FAIL) { + return; + } // Check in $PATH and also check directly if there is a directory name - rettv->vval.v_number = os_can_exe(name, NULL, true); + rettv->vval.v_number = os_can_exe(tv_get_string(&argvars[0]), NULL, true); } typedef struct { @@ -1984,10 +1985,13 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "exepath()" function static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const char *arg = tv_get_string(&argvars[0]); + if (tv_check_for_string(&argvars[0]) == FAIL) { + return; + } + char *path = NULL; - (void)os_can_exe(arg, &path, true); + (void)os_can_exe(tv_get_string(&argvars[0]), &path, true); rettv->v_type = VAR_STRING; rettv->vval.v_string = (char_u *)path; @@ -2161,7 +2165,7 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) .nextcmd = NULL, .cmdidx = CMD_USER, }; - eap.argt |= NOSPC; + eap.argt |= EX_NOSPC; expand_filename(&eap, &cmdstr, &errormsg); if (errormsg != NULL && *errormsg != NUL) { @@ -2459,7 +2463,7 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *fbuf = NULL; - size_t len; + size_t len = 0; char buf[NUMBUFLEN]; const char *fname = tv_get_string_chk(&argvars[0]); const char *const mods = tv_get_string_buf_chk(&argvars[1], buf); @@ -2468,8 +2472,10 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { len = strlen(fname); size_t usedlen = 0; - (void)modify_fname((char_u *)mods, false, &usedlen, - (char_u **)&fname, &fbuf, &len); + if (mods != NULL && *mods != NUL) { + (void)modify_fname((char_u *)mods, false, &usedlen, + (char_u **)&fname, &fbuf, &len); + } } rettv->v_type = VAR_STRING; @@ -3477,6 +3483,25 @@ static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_qf_loc_list(false, wp, &argvars[1], rettv); } + +/// "getmarklist()" function +static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (argvars[0].v_type == VAR_UNKNOWN) { + get_global_marks(rettv->vval.v_list); + return; + } + + buf_T *buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + return; + } + + get_buf_local_marks(buf, rettv->vval.v_list); +} + /* * "getmatches()" function */ @@ -4200,6 +4225,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"); @@ -6545,7 +6572,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 @@ -10847,7 +10874,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); } /* @@ -11213,6 +11240,13 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "windowsversion()" function +static void f_windowsversion(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *)xstrdup(windowsVersion); +} + /// "wordcount()" function static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index b62820fecc..02d32a4f86 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2953,6 +2953,19 @@ float_T tv_get_float(const typval_T *const tv) return 0; } +// Give an error and return FAIL unless "tv" is a non-empty string. +int tv_check_for_string(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (tv->v_type != VAR_STRING + || tv->vval.v_string == NULL + || *tv->vval.v_string == NUL) { + EMSG(_(e_stringreq)); + return FAIL; + } + return OK; +} + /// Get the string value of a "stringish" VimL object. /// /// @param[in] tv Object to get value of. diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 1e3e9bd366..d8ede1e3ba 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -492,7 +492,7 @@ static inline void tv_list_ref(list_T *const l) static inline void tv_list_set_ret(typval_T *const tv, list_T *const l) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); -/// Set a list as the return value +/// Set a list as the return value. Increments the reference count. /// /// @param[out] tv Object to receive the list /// @param[in,out] l List to pass to the object diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 8daef00985..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; } @@ -2484,6 +2484,7 @@ errret_2: ga_clear_strings(&newlines); ret_free: xfree(skip_until); + xfree(heredoc_trimmed); xfree(line_to_free); xfree(fudi.fd_newkey); xfree(name); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b0a51eaefd..61e4d634c6 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -629,6 +629,8 @@ void ex_sort(exarg_T *eap) if (sort_abort) goto sortend; + bcount_t old_count = 0, new_count = 0; + // Insert the lines in the sorted order below the last one. lnum = eap->line2; for (i = 0; i < count; i++) { @@ -641,6 +643,8 @@ void ex_sort(exarg_T *eap) } s = ml_get(get_lnum); + size_t bytelen = STRLEN(s) + 1; // include EOL in bytelen + old_count += bytelen; if (!unique || i == 0 || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) { // Copy the line into a buffer, it may become invalid in @@ -649,6 +653,7 @@ void ex_sort(exarg_T *eap) if (ml_append(lnum++, sortbuf1, (colnr_T)0, false) == FAIL) { break; } + new_count += bytelen; } fast_breakcheck(); if (got_int) @@ -668,11 +673,16 @@ void ex_sort(exarg_T *eap) deleted = (long)(count - (lnum - eap->line2)); if (deleted > 0) { mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted, - kExtmarkUndo); + kExtmarkNOOP); msgmore(-deleted); } else if (deleted < 0) { - mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, kExtmarkUndo); + mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, kExtmarkNOOP); } + + extmark_splice(curbuf, eap->line1-1, 0, + count, 0, old_count, + lnum - eap->line2, 0, new_count, kExtmarkUndo); + if (change_occurred || deleted != 0) { changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true); } @@ -1032,14 +1042,15 @@ void free_prev_shellcmd(void) * Bangs in the argument are replaced with the previously entered command. * Remember the argument. */ -void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) +void do_bang(int addr_count, exarg_T *eap, bool forceit, + bool do_in, bool do_out) + FUNC_ATTR_NONNULL_ALL { - char_u *arg = eap->arg; /* command */ - linenr_T line1 = eap->line1; /* start of range */ - linenr_T line2 = eap->line2; /* end of range */ - char_u *newcmd = NULL; /* the new command */ - int free_newcmd = FALSE; /* need to free() newcmd */ - int ins_prevcmd; + char_u *arg = eap->arg; // command + linenr_T line1 = eap->line1; // start of range + linenr_T line2 = eap->line2; // end of range + char_u *newcmd = NULL; // the new command + bool free_newcmd = false; // need to free() newcmd char_u *t; char_u *p; char_u *trailarg; @@ -1064,7 +1075,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) * Try to find an embedded bang, like in :!<cmd> ! [args] * (:!! is indicated by the 'forceit' variable) */ - ins_prevcmd = forceit; + bool ins_prevcmd = forceit; trailarg = arg; do { len = (int)STRLEN(trailarg) + 1; @@ -1101,7 +1112,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) else { trailarg = p; *trailarg++ = NUL; - ins_prevcmd = TRUE; + ins_prevcmd = true; break; } } @@ -1131,7 +1142,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) STRCPY(newcmd, p_shq); STRCAT(newcmd, prevcmd); STRCAT(newcmd, p_shq); - free_newcmd = TRUE; + free_newcmd = true; } if (addr_count == 0) { /* :! */ /* echo the command */ @@ -1164,15 +1175,15 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) // do this. // Alternatively, if on Unix and redirecting input or output, but not both, // and the 'shelltemp' option isn't set, use pipes. -// We use input redirection if do_in is TRUE. -// We use output redirection if do_out is TRUE. +// We use input redirection if do_in is true. +// We use output redirection if do_out is true. static void do_filter( linenr_T line1, linenr_T line2, exarg_T *eap, /* for forced 'ff' and 'fenc' */ char_u *cmd, - int do_in, - int do_out) + bool do_in, + bool do_out) { char_u *itmp = NULL; char_u *otmp = NULL; @@ -1669,10 +1680,17 @@ void ex_update(exarg_T *eap) */ void ex_write(exarg_T *eap) { - if (eap->usefilter) /* input lines to shell command */ - do_bang(1, eap, FALSE, TRUE, FALSE); - else + if (eap->cmdidx == CMD_saveas) { + // :saveas does not take a range, uses all lines. + eap->line1 = 1; + eap->line2 = curbuf->b_ml.ml_line_count; + } + + if (eap->usefilter) { // input lines to shell command + do_bang(1, eap, false, true, false); + } else { (void)do_write(eap); + } } /* @@ -2066,19 +2084,20 @@ static int check_readonly(int *forceit, buf_T *buf) return FALSE; } -/* - * Try to abandon current file and edit a new or existing file. - * "fnum" is the number of the file, if zero use ffname/sfname. - * "lnum" is the line number for the cursor in the new file (if non-zero). - * - * Return: - * GETFILE_ERROR for "normal" error, - * GETFILE_NOT_WRITTEN for "not written" error, - * GETFILE_SAME_FILE for success - * GETFILE_OPEN_OTHER for successfully opening another file. - */ -int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum, int forceit) +// Try to abandon the current file and edit a new or existing file. +// "fnum" is the number of the file, if zero use "ffname_arg"/"sfname_arg". +// "lnum" is the line number for the cursor in the new file (if non-zero). +// +// Return: +// GETFILE_ERROR for "normal" error, +// GETFILE_NOT_WRITTEN for "not written" error, +// GETFILE_SAME_FILE for success +// GETFILE_OPEN_OTHER for successfully opening another file. +int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, + linenr_T lnum, int forceit) { + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; int other; int retval; char_u *free_me = NULL; @@ -2322,7 +2341,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) { @@ -2705,7 +2724,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 @@ -2795,9 +2814,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 @@ -3704,7 +3724,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; @@ -3909,17 +3929,13 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, ADJUST_SUB_FIRSTLNUM(); - // TODO(bfredl): adjust also in preview, because decorations? - // this has some robustness issues, will look into later. - bool do_splice = !preview; + // TODO(bfredl): this has some robustness issues, look into later. bcount_t replaced_bytes = 0; lpos_T start = regmatch.startpos[0], end = regmatch.endpos[0]; - if (do_splice) { - for (i = 0; i < nmatch-1; i++) { - replaced_bytes += STRLEN(ml_get(lnum_start+i)) + 1; - } - replaced_bytes += end.col - start.col; + for (i = 0; i < nmatch-1; i++) { + replaced_bytes += STRLEN(ml_get(lnum_start+i)) + 1; } + replaced_bytes += end.col - start.col; // Now the trick is to replace CTRL-M chars with a real line @@ -3964,14 +3980,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, current_match.end.col = new_endcol; current_match.end.lnum = lnum; - if (do_splice) { - int matchcols = end.col - ((end.lnum == start.lnum) - ? start.col : 0); - int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0); - extmark_splice(curbuf, lnum_start-1, start_col, - end.lnum-start.lnum, matchcols, replaced_bytes, - lnum-lnum_start, subcols, sublen-1, kExtmarkUndo); - } + int matchcols = end.col - ((end.lnum == start.lnum) + ? start.col : 0); + int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0); + extmark_splice(curbuf, lnum_start-1, start_col, + end.lnum-start.lnum, matchcols, replaced_bytes, + lnum-lnum_start, subcols, sublen-1, kExtmarkUndo); } @@ -5254,8 +5268,10 @@ void ex_viusage(exarg_T *eap) /// @param tagname Name of the tags file ("tags" for English, "tags-fr" for /// French) /// @param add_help_tags Whether to add the "help-tags" tag -static void helptags_one(char_u *const dir, const char_u *const ext, - const char_u *const tagfname, const bool add_help_tags) +/// @param ignore_writeerr ignore write error +static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, + bool add_help_tags, bool ignore_writeerr) + FUNC_ATTR_NONNULL_ALL { garray_T ga; int filecount; @@ -5299,7 +5315,9 @@ static void helptags_one(char_u *const dir, const char_u *const ext, FILE *const fd_tags = os_fopen((char *)NameBuff, "w"); if (fd_tags == NULL) { - EMSG2(_("E152: Cannot open %s for writing"), NameBuff); + if (!ignore_writeerr) { + EMSG2(_("E152: Cannot open %s for writing"), NameBuff); + } FreeWild(filecount, files); return; } @@ -5447,7 +5465,9 @@ static void helptags_one(char_u *const dir, const char_u *const ext, } /// Generate tags in one help directory, taking care of translations. -static void do_helptags(char_u *dirname, bool add_help_tags) +static void do_helptags(char_u *dirname, bool add_help_tags, + bool ignore_writeerr) + FUNC_ATTR_NONNULL_ALL { int len; garray_T ga; @@ -5529,17 +5549,17 @@ static void do_helptags(char_u *dirname, bool add_help_tags) ext[1] = fname[5]; ext[2] = fname[6]; } - helptags_one(dirname, ext, fname, add_help_tags); + helptags_one(dirname, ext, fname, add_help_tags, ignore_writeerr); } ga_clear(&ga); FreeWild(filecount, files); } - static void -helptags_cb(char_u *fname, void *cookie) +static void helptags_cb(char_u *fname, void *cookie) + FUNC_ATTR_NONNULL_ALL { - do_helptags(fname, *(bool *)cookie); + do_helptags(fname, *(bool *)cookie, true); } /* @@ -5568,7 +5588,7 @@ void ex_helptags(exarg_T *eap) if (dirname == NULL || !os_isdir(dirname)) { EMSG2(_("E150: Not a directory: %s"), eap->arg); } else { - do_helptags(dirname, add_help_tags); + do_helptags(dirname, add_help_tags, false); } xfree(dirname); } @@ -5739,7 +5759,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_cmds.lua b/src/nvim/ex_cmds.lua index d62b00fee1..069d5d461b 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1,6 +1,9 @@ local bit = require 'bit' +local module = {} + -- Description of the values below is contained in ex_cmds_defs.h file. +-- "EX_" prefix is omitted. local RANGE = 0x001 local BANG = 0x002 local EXTRA = 0x004 @@ -14,3289 +17,3308 @@ local REGSTR = 0x200 local COUNT = 0x400 local NOTRLCOM = 0x800 local ZEROR = 0x1000 -local USECTRLV = 0x2000 -local NOTADR = 0x4000 -local EDITCMD = 0x8000 -local BUFNAME = 0x10000 -local BUFUNL = 0x20000 -local ARGOPT = 0x40000 -local SBOXOK = 0x80000 -local CMDWIN = 0x100000 -local MODIFY = 0x200000 -local EXFLAGS = 0x400000 -local RESTRICT = 0x800000 +local CTRLV = 0x2000 +local CMDARG = 0x4000 +local BUFNAME = 0x8000 +local BUFUNL = 0x10000 +local ARGOPT = 0x20000 +local SBOXOK = 0x40000 +local CMDWIN = 0x80000 +local MODIFY = 0x100000 +local FLAGS = 0x200000 local FILES = bit.bor(XFILE, EXTRA) local WORD1 = bit.bor(EXTRA, NOSPC) local FILE1 = bit.bor(FILES, NOSPC) -local ADDR_LINES = 0 -- buffer line numbers -local ADDR_WINDOWS = 1 -- window number -local ADDR_ARGUMENTS = 2 -- argument number -local ADDR_LOADED_BUFFERS = 3 -- buffer number of loaded buffer -local ADDR_BUFFERS = 4 -- buffer number -local ADDR_TABS = 5 -- tab page number -local ADDR_TABS_RELATIVE = 6 -- Tab page that only relative -local ADDR_QUICKFIX = 7 -- quickfix list entry number -local ADDR_OTHER = 99 -- something else +module.flags = { + RANGE = RANGE, + DFLALL = DFLALL, +} -- The following table is described in ex_cmds_defs.h file. -return { +module.cmds = { { command='append', flags=bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_append', }, { command='abbreviate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='abclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_abclear', }, { command='aboveleft', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='all', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_all', }, { command='amenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='anoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='args', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_args', }, { command='argadd', - flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, FILES, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, RANGE, ZEROR, FILES, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argadd', }, { command='argdelete', - flags=bit.bor(BANG, RANGE, NOTADR, FILES, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, RANGE, FILES, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argdelete', }, { command='argdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_ARGUMENTS', func='ex_listdo', }, { command='argedit', - flags=bit.bor(BANG, NEEDARG, RANGE, NOTADR, ZEROR, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, NEEDARG, RANGE, ZEROR, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argedit', }, { command='argglobal', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_args', }, { command='arglocal', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_args', }, { command='argument', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, RANGE, COUNT, EXTRA, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argument', }, { command='ascii', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='do_ascii', }, { command='autocmd', - flags=bit.bor(BANG, EXTRA, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_autocmd', }, { command='augroup', flags=bit.bor(BANG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_autocmd', }, { command='aunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='buffer', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, BUFUNL, COUNT, EXTRA, EDITCMD, TRLBAR), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR), + addr_type='ADDR_BUFFERS', func='ex_buffer', }, { command='bNext', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bprevious', }, { command='ball', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_buffer_all', }, { command='badd', - flags=bit.bor(NEEDARG, FILE1, EDITCMD, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN), + addr_type='ADDR_NONE', func='ex_edit', }, { command='bdelete', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_BUFFERS', func='ex_bunload', }, { command='behave', flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_behave', }, { command='belowright', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='bfirst', - flags=bit.bor(BANG, RANGE, NOTADR, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_brewind', }, { command='blast', - flags=bit.bor(BANG, RANGE, NOTADR, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_blast', }, { command='bmodified', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bmodified', }, { command='bnext', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bnext', }, { command='botright', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='bprevious', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bprevious', }, { command='brewind', - flags=bit.bor(BANG, RANGE, NOTADR, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_brewind', }, { command='break', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_break', }, { command='breakadd', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_breakadd', }, { command='breakdel', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_breakdel', }, { command='breaklist', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_breaklist', }, { command='browse', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='buffers', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='buflist_list', }, { command='bufdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_BUFFERS', func='ex_listdo', }, { command='bunload', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_LOADED_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_LOADED_BUFFERS', func='ex_bunload', }, { command='bwipeout', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, BUFUNL, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_BUFFERS', func='ex_bunload', }, { command='change', flags=bit.bor(BANG, WHOLEFOLD, RANGE, COUNT, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_change', }, { command='cNext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cNfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='cabclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_abclear', }, { command='cabove', - flags=bit.bor(RANGE, TRLBAR), - addr_type=ADDR_OTHER , + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='ex_cbelow', }, { command='caddbuffer', - flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { command='caddexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='caddfile', flags=bit.bor(TRLBAR, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, { + command='cafter', + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', + func='ex_cbelow', + }, + { command='call', flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_call', }, { command='catch', flags=bit.bor(EXTRA, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_catch', }, { command='cbuffer', - flags=bit.bor(BANG, RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { + command='cbefore', + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', + func='ex_cbelow', + }, + { command='cbelow', - flags=bit.bor(RANGE, TRLBAR), - addr_type=ADDR_OTHER , + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='ex_cbelow', }, { command='cbottom', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cbottom', }, { command='cc', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_QUICKFIX', func='ex_cc', }, { command='cclose', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(TRLBAR), + addr_type='ADDR_NONE', func='ex_cclose', }, { command='cd', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='cdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_QUICKFIX, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_QUICKFIX_VALID', func='ex_listdo', }, { command='center', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_align', }, { command='cexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='cfile', flags=bit.bor(TRLBAR, FILE1, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, -- Even though 'cfdo' is alphabetically lower than 'cfile', it is after -- 'cfile' in this cmd list to support the existing ":cf" abbreviation. { command='cfdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_QUICKFIX, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_QUICKFIX_VALID', func='ex_listdo', }, { command='cfirst', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='cgetfile', flags=bit.bor(TRLBAR, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, { command='cgetbuffer', - flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { command='cgetexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='chdir', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='changes', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_changes', }, { command='checkhealth', flags=bit.bor(EXTRA, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_checkhealth', }, { command='checkpath', flags=bit.bor(TRLBAR, BANG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_checkpath', }, { command='checktime', - flags=bit.bor(RANGE, NOTADR, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', func='ex_checktime', }, { command='chistory', - flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_history', }, { command='clist', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='qf_list', }, { command='clast', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='close', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR, CMDWIN), - addr_type=ADDR_WINDOWS, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN), + addr_type='ADDR_WINDOWS', func='ex_close', }, { command='clearjumps', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_clearjumps', }, { command='cmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='cmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='cmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='cnext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cnewer', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_age', }, { command='cnfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='cnoreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='cnoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='copy', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_copymove', }, { command='colder', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_age', }, { command='colorscheme', flags=bit.bor(WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_colorscheme', }, { command='command', - flags=bit.bor(EXTRA, BANG, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_command', }, { command='comclear', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_comclear', }, { command='compiler', flags=bit.bor(BANG, TRLBAR, WORD1, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_compiler', }, { command='continue', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_continue', }, { command='confirm', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='const', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_const', }, { command='copen', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_copen', }, { command='cprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cpfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_OTHER', func='ex_cnext', }, { command='cquit', - flags=bit.bor(RANGE, NOTADR, COUNT, ZEROR, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, ZEROR, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cquit', }, { command='crewind', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='cscope', flags=bit.bor(EXTRA, NOTRLCOM, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cscope', }, { command='cstag', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cstag', }, { command='cunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='cunabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='cunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='cwindow', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cwindow', }, { command='delete', flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_operators', }, { command='delmarks', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_delmarks', }, { command='debug', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_debug', }, { command='debuggreedy', - flags=bit.bor(RANGE, NOTADR, ZEROR, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, TRLBAR, CMDWIN), + addr_type='ADDR_OTHER', func='ex_debuggreedy', }, { command='delcommand', flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_delcommand', }, { command='delfunction', flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_delfunction', }, { command='display', flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_display', }, { command='diffupdate', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffupdate', }, { command='diffget', flags=bit.bor(RANGE, EXTRA, TRLBAR, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_diffgetput', }, { command='diffoff', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffoff', }, { command='diffpatch', flags=bit.bor(EXTRA, FILE1, TRLBAR, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffpatch', }, { command='diffput', flags=bit.bor(RANGE, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_diffgetput', }, { command='diffsplit', flags=bit.bor(EXTRA, FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffsplit', }, { command='diffthis', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffthis', }, { command='digraphs', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_digraphs', }, { command='djump', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='dlist', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='doautocmd', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_doautocmd', }, { command='doautoall', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_doautoall', }, { command='drop', - flags=bit.bor(FILES, EDITCMD, NEEDARG, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_drop', }, { command='dsearch', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='dsplit', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='edit', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_edit', }, { command='earlier', flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_later', }, { command='echo', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_echo', }, { command='echoerr', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_execute', }, { command='echohl', flags=bit.bor(EXTRA, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_echohl', }, { command='echomsg', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_execute', }, { command='echon', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_echo', }, { command='else', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_else', }, { command='elseif', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_else', }, { command='emenu', - flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, NOTADR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, CMDWIN), + addr_type='ADDR_OTHER', func='ex_emenu', }, { command='endif', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endif', }, { command='endfunction', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endfunction', }, { command='endfor', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endwhile', }, { command='endtry', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endtry', }, { command='endwhile', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endwhile', }, { command='enew', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_edit', }, { command='ex', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_edit', }, { command='execute', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_execute', }, { command='exit', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_exit', }, { command='exusage', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_exusage', }, { command='file', - flags=bit.bor(RANGE, NOTADR, ZEROR, BANG, FILE1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, BANG, FILE1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_file', }, { command='files', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='buflist_list', }, { command='filetype', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_filetype', }, { command='filter', flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='find', - flags=bit.bor(RANGE, NOTADR, BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILE1, CMDARG, ARGOPT, TRLBAR, NEEDARG), + addr_type='ADDR_OTHER', func='ex_find', }, { command='finally', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_finally', }, { command='finish', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_finish', }, { command='first', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_rewind', }, { command='fold', flags=bit.bor(RANGE, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_fold', }, { command='foldclose', flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_foldopen', }, { command='folddoopen', flags=bit.bor(RANGE, DFLALL, NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_folddo', }, { command='folddoclosed', flags=bit.bor(RANGE, DFLALL, NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_folddo', }, { command='foldopen', flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_foldopen', }, { command='for', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_while', }, { command='function', flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_function', }, { command='global', flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, DFLALL, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_global', }, { command='goto', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, SBOXOK, CMDWIN), + addr_type='ADDR_OTHER', func='ex_goto', }, { command='grep', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_make', }, { command='grepadd', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_make', }, { command='gui', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN), + addr_type='ADDR_NONE', func='ex_nogui', }, { command='gvim', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN), + addr_type='ADDR_NONE', func='ex_nogui', }, { command='help', flags=bit.bor(BANG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_help', }, { command='helpclose', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_helpclose', }, { command='helpgrep', flags=bit.bor(EXTRA, NOTRLCOM, NEEDARG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_helpgrep', }, { command='helptags', flags=bit.bor(NEEDARG, FILES, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_helptags', }, { command='hardcopy', flags=bit.bor(RANGE, COUNT, EXTRA, TRLBAR, DFLALL, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_hardcopy', }, { command='highlight', flags=bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_highlight', }, { command='hide', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_WINDOWS, + flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_WINDOWS', func='ex_hide', }, { command='history', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_history', }, { command='insert', flags=bit.bor(BANG, RANGE, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_append', }, { command='iabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='iabclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_abclear', }, { command='if', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_if', }, { command='ijump', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='ilist', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='imap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='imapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='imenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='inoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='inoreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='inoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='intro', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_intro', }, { command='isearch', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='isplit', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='iunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='iunabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='iunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='join', - flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY), + addr_type='ADDR_LINES', func='ex_join', }, { command='jumps', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_jumps', }, { command='k', flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_mark', }, { command='keepmarks', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='keepjumps', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='keeppatterns', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='keepalt', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='list', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN), + addr_type='ADDR_LINES', func='ex_print', }, { command='lNext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='lNfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='last', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_last', }, { command='labove', - flags=bit.bor(RANGE, TRLBAR), - addr_type=ADDR_OTHER , + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='ex_cbelow', }, { command='language', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_language', }, { command='laddexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='laddbuffer', - flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { command='laddfile', flags=bit.bor(TRLBAR, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, { + command='lafter', + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', + func='ex_cbelow', + }, + { command='later', flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_later', }, { command='lbuffer', - flags=bit.bor(BANG, RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { + command='lbefore', + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', + func='ex_cbelow', + }, + { command='lbelow', - flags=bit.bor(RANGE, TRLBAR), - addr_type=ADDR_OTHER , + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='ex_cbelow', }, { command='lbottom', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cbottom', }, { command='lcd', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='lchdir', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='lclose', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cclose', }, { command='lcscope', flags=bit.bor(EXTRA, NOTRLCOM, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cscope', }, { command='ldo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_QUICKFIX, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_QUICKFIX_VALID', func='ex_listdo', }, { command='left', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_align', }, { command='leftabove', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='let', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_let', }, { command='lexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='lfile', flags=bit.bor(TRLBAR, FILE1, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, -- Even though 'lfdo' is alphabetically lower than 'lfile', it is after -- 'lfile' in this cmd list to support the existing ":lf" abbreviation. { command='lfdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_QUICKFIX, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_QUICKFIX_VALID', func='ex_listdo', }, { command='lfirst', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='lgetfile', flags=bit.bor(TRLBAR, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, { command='lgetbuffer', - flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { command='lgetexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='lgrep', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_make', }, { command='lgrepadd', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_make', }, { command='lhelpgrep', flags=bit.bor(EXTRA, NOTRLCOM, NEEDARG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_helpgrep', }, { command='lhistory', - flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_history', }, { command='ll', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_QUICKFIX', func='ex_cc', }, { command='llast', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='llist', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='qf_list', }, { command='lmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='lmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='lmake', flags=bit.bor(BANG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_make', }, { command='lnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='lnext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='lnewer', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_age', }, { command='lnfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='loadview', flags=bit.bor(FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_loadview', }, { command='loadkeymap', flags=bit.bor(CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_loadkeymap', }, { command='lockmarks', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='lockvar', flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_lockvar', }, { command='lolder', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_age', }, { command='lopen', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_copen', }, { command='lprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='lpfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_OTHER', func='ex_cnext', }, { command='lrewind', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='ltag', - flags=bit.bor(NOTADR, TRLBAR, BANG, WORD1), - addr_type=ADDR_LINES, + flags=bit.bor(TRLBAR, BANG, WORD1), + addr_type='ADDR_NONE', func='ex_tag', }, { command='lunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='lua', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_lua', }, { command='luado', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_luado', }, { command='luafile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_luafile', }, { command='lvimgrep', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_vimgrep', }, { command='lvimgrepadd', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_vimgrep', }, { command='lwindow', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cwindow', }, { command='ls', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='buflist_list', }, { command='move', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_copymove', }, { command='mark', flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_mark', }, { command='make', flags=bit.bor(BANG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_make', }, { command='map', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='mapclear', flags=bit.bor(EXTRA, BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='marks', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_marks', }, { command='match', - flags=bit.bor(RANGE, NOTADR, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, CMDWIN), + addr_type='ADDR_OTHER', func='ex_match', }, { command='menu', - flags=bit.bor(RANGE, NOTADR, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='menutranslate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menutranslate', }, { command='messages', flags=bit.bor(EXTRA, TRLBAR, RANGE, CMDWIN), - addr_type=ADDR_OTHER, + addr_type='ADDR_OTHER', func='ex_messages', }, { command='mkexrc', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkrc', }, { command='mksession', flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkrc', }, { command='mkspell', flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkspell', }, { command='mkvimrc', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkrc', }, { command='mkview', flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkrc', }, { command='mode', flags=bit.bor(WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mode', }, { command='mzscheme', flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_script_ni', }, { command='mzfile', flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_ni', }, { command='next', - flags=bit.bor(RANGE, NOTADR, BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_next', }, { command='new', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='nmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='nmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='nmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='nnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='nnoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='noremap', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='noautocmd', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='nohlsearch', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_nohlsearch', }, { command='noreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='noremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='noswapfile', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='normal', - flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, USECTRLV, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, CTRLV, SBOXOK, CMDWIN), + addr_type='ADDR_LINES', func='ex_normal', }, { command='number', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN), + addr_type='ADDR_LINES', func='ex_print', }, { command='nunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='nunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='oldfiles', flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_oldfiles', }, { command='omap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='omapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='omenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='only', - flags=bit.bor(BANG, NOTADR, RANGE, COUNT, TRLBAR), - addr_type=ADDR_WINDOWS, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type='ADDR_WINDOWS', func='ex_only', }, { command='onoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='onoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='options', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_options', }, { command='ounmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='ounmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='ownsyntax', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ownsyntax', }, { command='print', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, SBOXOK), + addr_type='ADDR_LINES', func='ex_print', }, { command='packadd', flags=bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_packadd', }, { command='packloadall', flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_packloadall', }, { command='pclose', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_pclose', }, { command='perl', - flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN), + addr_type='ADDR_LINES', func='ex_perl', }, { command='perldo', - flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_perldo', }, { command='perlfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_perlfile', }, { command='pedit', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_pedit', }, { command='pop', - flags=bit.bor(RANGE, NOTADR, BANG, COUNT, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, COUNT, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='popup', flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ni', }, { command='ppop', - flags=bit.bor(RANGE, NOTADR, BANG, COUNT, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, COUNT, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='preserve', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_preserve', }, { command='previous', - flags=bit.bor(EXTRA, RANGE, NOTADR, COUNT, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_previous', }, { command='profile', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_profile', }, { command='profdel', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_breakdel', }, { command='psearch', flags=bit.bor(BANG, RANGE, WHOLEFOLD, DFLALL, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_psearch', }, { command='ptag', - flags=bit.bor(RANGE, NOTADR, BANG, WORD1, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptNext', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptfirst', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptjump', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ptag', }, { command='ptlast', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ptag', }, { command='ptnext', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptprevious', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptrewind', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptselect', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ptag', }, { command='put', flags=bit.bor(RANGE, WHOLEFOLD, BANG, REGSTR, TRLBAR, ZEROR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_put', }, { command='pwd', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_pwd', }, { command='python', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_python', }, { command='pydo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pydo', }, { command='pyfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyfile', }, { command='py3', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_python3', }, { command='py3do', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pydo3', }, { command='python3', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_python3', }, { command='py3file', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_py3file', }, { command='pyx', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyx', }, { command='pyxdo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyxdo', }, { command='pythonx', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyx', }, { command='pyxfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyxfile', }, { command='quit', - flags=bit.bor(BANG, RANGE, COUNT, NOTADR, TRLBAR, CMDWIN), - addr_type=ADDR_WINDOWS, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN), + addr_type='ADDR_WINDOWS', func='ex_quit', }, { command='quitall', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_quit_all', }, { command='qall', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_quit_all', }, { command='read', flags=bit.bor(BANG, RANGE, WHOLEFOLD, FILE1, ARGOPT, TRLBAR, ZEROR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_read', }, { command='recover', flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_recover', }, { command='redo', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redo', }, { command='redir', flags=bit.bor(BANG, FILES, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redir', }, { command='redraw', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redraw', }, { command='redrawstatus', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redrawstatus', }, { command='redrawtabline', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redrawtabline', }, { command='registers', flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_display', }, { command='resize', - flags=bit.bor(RANGE, NOTADR, TRLBAR, WORD1, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, TRLBAR, WORD1, CMDWIN), + addr_type='ADDR_OTHER', func='ex_resize', }, { command='retab', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, DFLALL, BANG, WORD1, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_retab', }, { command='return', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_return', }, { command='rewind', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_rewind', }, { command='right', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_align', }, { command='rightbelow', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='rshada', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_shada', }, { command='runtime', flags=bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_runtime', }, { command='rundo', flags=bit.bor(NEEDARG, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_rundo', }, { command='ruby', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_ruby', }, { command='rubydo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_rubydo', }, { command='rubyfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_rubyfile', }, { command='rviminfo', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_shada', }, { command='substitute', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_substitute', }, { command='sNext', - flags=bit.bor(EXTRA, RANGE, NOTADR, COUNT, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_previous', }, { command='sargument', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, RANGE, COUNT, EXTRA, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argument', }, { command='sall', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_all', }, { command='sandbox', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='saveas', - flags=bit.bor(BANG, DFLALL, FILE1, ARGOPT, CMDWIN, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, ARGOPT, CMDWIN, TRLBAR), + addr_type='ADDR_NONE', func='ex_write', }, { command='sbuffer', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, BUFUNL, COUNT, EXTRA, EDITCMD, TRLBAR), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR), + addr_type='ADDR_BUFFERS', func='ex_buffer', }, { command='sbNext', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bprevious', }, { command='sball', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_buffer_all', }, { command='sbfirst', - flags=bit.bor(EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(CMDARG, TRLBAR), + addr_type='ADDR_NONE', func='ex_brewind', }, { command='sblast', - flags=bit.bor(EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(CMDARG, TRLBAR), + addr_type='ADDR_NONE', func='ex_blast', }, { command='sbmodified', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bmodified', }, { command='sbnext', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bnext', }, { command='sbprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bprevious', }, { command='sbrewind', - flags=bit.bor(EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(CMDARG, TRLBAR), + addr_type='ADDR_NONE', func='ex_brewind', }, { command='scriptnames', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR, CMDWIN), - addr_type=ADDR_OTHER, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN), + addr_type='ADDR_OTHER', func='ex_scriptnames', }, { command='scriptencoding', flags=bit.bor(WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_scriptencoding', }, { command='scscope', flags=bit.bor(EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_scscope', }, { command='set', flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_set', }, { command='setfiletype', flags=bit.bor(TRLBAR, EXTRA, NEEDARG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_setfiletype', }, { command='setglobal', flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_set', }, { command='setlocal', flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_set', }, { command='sfind', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR, NEEDARG), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='sfirst', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_rewind', }, { command='simalt', flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ni', }, { command='sign', - flags=bit.bor(NEEDARG, RANGE, NOTADR, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(NEEDARG, RANGE, EXTRA, CMDWIN), + addr_type='ADDR_OTHER', func='ex_sign', }, { command='silent', flags=bit.bor(NEEDARG, EXTRA, BANG, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='sleep', - flags=bit.bor(RANGE, NOTADR, COUNT, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, EXTRA, TRLBAR, CMDWIN), + addr_type='ADDR_OTHER', func='ex_sleep', }, { command='slast', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_last', }, { command='smagic', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_submagic', }, { command='smap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='smapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='smenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='snext', - flags=bit.bor(RANGE, NOTADR, BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_next', }, { command='snomagic', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_submagic', }, { command='snoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='snoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='source', flags=bit.bor(BANG, FILE1, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_source', }, { command='sort', flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, EXTRA, NOTRLCOM, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_sort', }, { command='split', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='spellgood', - flags=bit.bor(BANG, RANGE, NOTADR, NEEDARG, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', func='ex_spell', }, { command='spelldump', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_spelldump', }, { command='spellinfo', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_spellinfo', }, { command='spellrepall', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_spellrepall', }, { command='spellundo', - flags=bit.bor(BANG, RANGE, NOTADR, NEEDARG, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', func='ex_spell', }, { command='spellwrong', - flags=bit.bor(BANG, RANGE, NOTADR, NEEDARG, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', func='ex_spell', }, { command='sprevious', - flags=bit.bor(EXTRA, RANGE, NOTADR, COUNT, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_previous', }, { command='srewind', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_rewind', }, { command='stop', flags=bit.bor(TRLBAR, BANG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stop', }, { command='stag', - flags=bit.bor(RANGE, NOTADR, BANG, WORD1, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_stag', }, { command='startinsert', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_startinsert', }, { command='startgreplace', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_startinsert', }, { command='startreplace', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_startinsert', }, { command='stopinsert', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stopinsert', }, { command='stjump', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stag', }, { command='stselect', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stag', }, { command='sunhide', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_buffer_all', }, { command='sunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='sunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='suspend', flags=bit.bor(TRLBAR, BANG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stop', }, { command='sview', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='swapname', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_swapname', }, { command='syntax', flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_syntax', }, { command='syntime', flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_syntime', }, { command='syncbind', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_syncbind', }, { command='t', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_copymove', }, { command='tcd', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='tchdir', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='tNext', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='tag', - flags=bit.bor(RANGE, NOTADR, BANG, WORD1, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='tags', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='do_tags', }, { command='tab', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='tabclose', - flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), + addr_type='ADDR_TABS', func='ex_tabclose', }, { command='tabdo', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_TABS, + flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_TABS', func='ex_listdo', }, { command='tabedit', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, ZEROR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_TABS', func='ex_splitview', }, { command='tabfind', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, ZEROR, EDITCMD, ARGOPT, NEEDARG, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, NEEDARG, TRLBAR), + addr_type='ADDR_TABS', func='ex_splitview', }, { command='tabfirst', flags=bit.bor(TRLBAR), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_tabnext', }, { command='tabmove', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type='ADDR_TABS', func='ex_tabmove', }, { command='tablast', flags=bit.bor(TRLBAR), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_tabnext', }, { command='tabnext', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type='ADDR_TABS', func='ex_tabnext', }, { command='tabnew', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, ZEROR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_TABS', func='ex_splitview', }, { command='tabonly', - flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), + addr_type='ADDR_TABS', func='ex_tabonly', }, { command='tabprevious', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type=ADDR_TABS_RELATIVE, + flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type='ADDR_TABS_RELATIVE', func='ex_tabnext', }, { command='tabNext', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type=ADDR_TABS_RELATIVE, + flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type='ADDR_TABS_RELATIVE', func='ex_tabnext', }, { command='tabrewind', flags=bit.bor(TRLBAR), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_tabnext', }, { command='tabs', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_tabs', }, { command='tcl', flags=bit.bor(RANGE,EXTRA,NEEDARG,CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_script_ni', }, { command='tcldo', flags=bit.bor(RANGE,DFLALL,EXTRA,NEEDARG,CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_ni', }, { command='tclfile', flags=bit.bor(RANGE,FILE1,NEEDARG,CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_ni', }, { command='terminal', flags=bit.bor(BANG, FILES, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_terminal', }, { command='tfirst', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='throw', flags=bit.bor(EXTRA, NEEDARG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_throw', }, { command='tjump', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_tag', }, { command='tlast', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_tag', }, { command='tmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='tmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='tmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='tnext', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='tnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='topleft', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='tprevious', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='trewind', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='try', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_try', }, { command='tselect', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_tag', }, { command='tunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='tunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='undo', - flags=bit.bor(RANGE, NOTADR, COUNT, ZEROR, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, ZEROR, TRLBAR, CMDWIN), + addr_type='ADDR_OTHER', func='ex_undo', }, { command='undojoin', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_undojoin', }, { command='undolist', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_undolist', }, { command='unabbreviate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='unhide', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_buffer_all', }, { command='unlet', flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_unlet', }, { command='unlockvar', flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_lockvar', }, { command='unmap', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='unmenu', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='unsilent', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='update', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_update', }, { command='vglobal', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, DFLALL, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_global', }, { command='version', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_version', }, { command='verbose', - flags=bit.bor(NEEDARG, RANGE, NOTADR, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(NEEDARG, RANGE, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), + addr_type='ADDR_OTHER', func='ex_wrongmodifier', }, { command='vertical', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='visual', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_edit', }, { command='view', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_edit', }, { command='vimgrep', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_vimgrep', }, { command='vimgrepadd', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_vimgrep', }, { command='viusage', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_viusage', }, { command='vmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='vmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='vmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='vnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='vnew', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='vnoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='vsplit', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='vunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='vunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='write', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_write', }, { command='wNext', - flags=bit.bor(RANGE, WHOLEFOLD, NOTADR, BANG, FILE1, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_wnext', }, { command='wall', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='do_wqall', }, { command='while', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_while', }, { command='winsize', flags=bit.bor(EXTRA, NEEDARG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_winsize', }, { command='wincmd', - flags=bit.bor(NEEDARG, WORD1, RANGE, NOTADR, CMDWIN), - addr_type=ADDR_WINDOWS, + flags=bit.bor(NEEDARG, WORD1, RANGE, CMDWIN), + addr_type='ADDR_OTHER', func='ex_wincmd', }, { command='windo', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_WINDOWS, + flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_WINDOWS', func='ex_listdo', }, { command='winpos', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ni', }, { command='wnext', - flags=bit.bor(RANGE, NOTADR, BANG, FILE1, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILE1, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_wnext', }, { command='wprevious', - flags=bit.bor(RANGE, NOTADR, BANG, FILE1, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILE1, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_wnext', }, { command='wq', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_exit', }, { command='wqall', - flags=bit.bor(BANG, FILE1, ARGOPT, DFLALL, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='do_wqall', }, { command='wshada', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_shada', }, { command='wundo', flags=bit.bor(BANG, NEEDARG, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wundo', }, { command='wviminfo', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_shada', }, { command='xit', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_exit', }, { command='xall', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='do_wqall', }, { command='xmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='xmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='xmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='xnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='xnoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='xunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='xunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='yank', flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_operators', }, { command='z', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, FLAGS, TRLBAR, CMDWIN), + addr_type='ADDR_LINES', func='ex_z', }, { command='!', enum='CMD_bang', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILES, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_bang', }, { command='#', enum='CMD_pound', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN), + addr_type='ADDR_LINES', func='ex_print', }, { command='&', enum='CMD_and', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_substitute', }, { command='<', enum='CMD_lshift', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY), + addr_type='ADDR_LINES', func='ex_operators', }, { command='=', enum='CMD_equal', - flags=bit.bor(RANGE, TRLBAR, DFLALL, EXFLAGS, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, TRLBAR, DFLALL, FLAGS, CMDWIN), + addr_type='ADDR_LINES', func='ex_equal', }, { command='>', enum='CMD_rshift', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY), + addr_type='ADDR_LINES', func='ex_operators', }, { command='@', enum='CMD_at', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_at', }, { command='Next', - flags=bit.bor(EXTRA, RANGE, NOTADR, COUNT, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_previous', }, { command='~', enum='CMD_tilde', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_substitute', }, } + +return module diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 3b9c44c3cd..c400975108 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2113,7 +2113,7 @@ void ex_listdo(exarg_T *eap) } } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { - qf_size = qf_get_size(eap); + qf_size = qf_get_valid_size(eap); assert(eap->line1 >= 0); if (qf_size == 0 || (size_t)eap->line1 > qf_size) { buf = NULL; @@ -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_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 21db3936b8..ca84d375ce 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -36,46 +36,50 @@ // 4. Add documentation in ../doc/xxx.txt. Add a tag for both the short and // long name of the command. -#define RANGE 0x001 // allow a linespecs -#define BANG 0x002 // allow a ! after the command name -#define EXTRA 0x004 // allow extra args after command name -#define XFILE 0x008 // expand wildcards in extra part -#define NOSPC 0x010 // no spaces allowed in the extra part -#define DFLALL 0x020 // default file range is 1,$ -#define WHOLEFOLD 0x040 // extend range to include whole fold also - // when less than two numbers given -#define NEEDARG 0x080 // argument required -#define TRLBAR 0x100 // check for trailing vertical bar -#define REGSTR 0x200 // allow "x for register designation -#define COUNT 0x400 // allow count in argument, after command -#define NOTRLCOM 0x800 // no trailing comment allowed -#define ZEROR 0x1000 // zero line number allowed -#define USECTRLV 0x2000 // do not remove CTRL-V from argument -#define NOTADR 0x4000 // number before command is not an address -#define EDITCMD 0x8000 // allow "+command" argument -#define BUFNAME 0x10000 // accepts buffer name -#define BUFUNL 0x20000 // accepts unlisted buffer too -#define ARGOPT 0x40000 // allow "++opt=val" argument -#define SBOXOK 0x80000 // allowed in the sandbox -#define CMDWIN 0x100000 // allowed in cmdline window; when missing - // disallows editing another buffer when - // curbuf_lock is set -#define MODIFY 0x200000 // forbidden in non-'modifiable' buffer -#define EXFLAGS 0x400000 // allow flags after count in argument -#define FILES (XFILE | EXTRA) // multiple extra files allowed -#define WORD1 (EXTRA | NOSPC) // one extra word allowed -#define FILE1 (FILES | NOSPC) // 1 file allowed, defaults to current file +#define EX_RANGE 0x001 // allow a linespecs +#define EX_BANG 0x002 // allow a ! after the command name +#define EX_EXTRA 0x004 // allow extra args after command name +#define EX_XFILE 0x008 // expand wildcards in extra part +#define EX_NOSPC 0x010 // no spaces allowed in the extra part +#define EX_DFLALL 0x020 // default file range is 1,$ +#define EX_WHOLEFOLD 0x040 // extend range to include whole fold also + // when less than two numbers given +#define EX_NEEDARG 0x080 // argument required +#define EX_TRLBAR 0x100 // check for trailing vertical bar +#define EX_REGSTR 0x200 // allow "x for register designation +#define EX_COUNT 0x400 // allow count in argument, after command +#define EX_NOTRLCOM 0x800 // no trailing comment allowed +#define EX_ZEROR 0x1000 // zero line number allowed +#define EX_CTRLV 0x2000 // do not remove CTRL-V from argument +#define EX_CMDARG 0x4000 // allow "+command" argument +#define EX_BUFNAME 0x8000 // accepts buffer name +#define EX_BUFUNL 0x10000 // accepts unlisted buffer too +#define EX_ARGOPT 0x20000 // allow "++opt=val" argument +#define EX_SBOXOK 0x40000 // allowed in the sandbox +#define EX_CMDWIN 0x80000 // allowed in cmdline window; when missing + // disallows editing another buffer when + // curbuf_lock is set +#define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer +#define EX_FLAGS 0x200000 // allow flags after count in argument +#define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed +#define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file +#define EX_WORD1 (EX_EXTRA | EX_NOSPC) // one extra word allowed // values for cmd_addr_type -#define ADDR_LINES 0 -#define ADDR_WINDOWS 1 -#define ADDR_ARGUMENTS 2 -#define ADDR_LOADED_BUFFERS 3 -#define ADDR_BUFFERS 4 -#define ADDR_TABS 5 -#define ADDR_TABS_RELATIVE 6 // Tab page that only relative -#define ADDR_QUICKFIX 7 -#define ADDR_OTHER 99 +typedef enum { + ADDR_LINES, // buffer line numbers + ADDR_WINDOWS, // window number + ADDR_ARGUMENTS, // argument number + ADDR_LOADED_BUFFERS, // buffer number of loaded buffer + ADDR_BUFFERS, // buffer number + ADDR_TABS, // tab page number + ADDR_TABS_RELATIVE, // Tab page that only relative + ADDR_QUICKFIX_VALID, // quickfix list valid entry number + ADDR_QUICKFIX, // quickfix list entry number + ADDR_UNSIGNED, // positive count or zero, defaults to 1 + ADDR_OTHER, // something else, use line number for '$', '%', etc. + ADDR_NONE // no range used +} cmd_addr_T; typedef struct exarg exarg_T; @@ -93,7 +97,7 @@ typedef struct cmdname { char_u *cmd_name; ///< Name of the command. ex_func_T cmd_func; ///< Function with implementation of this command. uint32_t cmd_argt; ///< Relevant flags from the declared above. - int cmd_addr_type; ///< Flag for address type + cmd_addr_T cmd_addr_type; ///< Flag for address type } CommandDefinition; // A list used for saving values of "emsg_silent". Used by ex_try() to save the @@ -150,7 +154,7 @@ struct exarg { int addr_count; ///< the number of addresses given linenr_T line1; ///< the first line number linenr_T line2; ///< the second line number or count - int addr_type; ///< type of the count/range + cmd_addr_T addr_type; ///< type of the count/range int flags; ///< extra flags after count: EXFLAG_ char_u *do_ecmd_cmd; ///< +command arg to be used in edited file linenr_T do_ecmd_lnum; ///< the line number in an edited file diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 0ffe67a4db..ccf7dd0f68 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -85,7 +85,7 @@ typedef struct ucmd { char_u *uc_rep; // The command's replacement string long uc_def; // The default value for a range/count int uc_compl; // completion type - int uc_addr_type; // The command's address type + cmd_addr_T uc_addr_type; // The command's address type sctx_T uc_script_ctx; // SCTX where the command was defined char_u *uc_compl_arg; // completion argument if any } ucmd_T; @@ -1086,7 +1086,10 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset) return buf->b_fnum; } -static int current_win_nr(win_T *win) +// Return the window number of "win". +// When "win" is NULL return the number of windows. +static int current_win_nr(const win_T *win) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { int nr = 0; @@ -1163,7 +1166,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) case 'd': case Ctrl_D: // window size or any count - eap->addr_type = ADDR_LINES; // -V1037 + eap->addr_type = ADDR_OTHER; // -V1037 break; case Ctrl_HAT: @@ -1199,7 +1202,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) case '=': case CAR: // no count - eap->addr_type = 0; + eap->addr_type = ADDR_NONE; break; } } @@ -1380,6 +1383,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (ea.cmdidx == CMD_wincmd && p != NULL) { get_wincmd_addr_type(skipwhite(p), &ea); } + // :.cc in quickfix window uses line number + if ((ea.cmdidx == CMD_cc || ea.cmdidx == CMD_ll) && bt_quickfix(curbuf)) { + ea.addr_type = ADDR_OTHER; + } } ea.cmd = cmd; @@ -1411,7 +1418,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, } if (*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2)) { ea.cmdidx = CMD_print; - ea.argt = RANGE | COUNT | TRLBAR; + ea.argt = EX_RANGE | EX_COUNT | EX_TRLBAR; if ((errormsg = invalid_range(&ea)) == NULL) { correct_range(&ea); ex_print(&ea); @@ -1490,20 +1497,18 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.forceit = false; } - /* - * 6. Parse arguments. - */ + // 6. Parse arguments. Then check for errors. if (!IS_USER_CMDIDX(ea.cmdidx)) { ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; } if (!ea.skip) { - if (sandbox != 0 && !(ea.argt & SBOXOK)) { + if (sandbox != 0 && !(ea.argt & EX_SBOXOK)) { // Command not allowed in sandbox. errormsg = (char_u *)_(e_sandbox); goto doend; } - if (!MODIFIABLE(curbuf) && (ea.argt & MODIFY) + if (!MODIFIABLE(curbuf) && (ea.argt & EX_MODIFY) // allow :put in terminals && (!curbuf->terminal || ea.cmdidx != CMD_put)) { /* Command not allowed in non-'modifiable' buffer */ @@ -1511,7 +1516,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } - if (text_locked() && !(ea.argt & CMDWIN) + if (text_locked() && !(ea.argt & EX_CMDWIN) && !IS_USER_CMDIDX(ea.cmdidx)) { // Command not allowed when editing the command line. errormsg = (char_u *)_(get_text_locked_msg()); @@ -1522,7 +1527,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, // Do allow ":checktime" (it is postponed). // Do allow ":edit" (check for an argument later). // Do allow ":file" with no arguments (check for an argument later). - if (!(ea.argt & CMDWIN) + if (!(ea.argt & EX_CMDWIN) && ea.cmdidx != CMD_checktime && ea.cmdidx != CMD_edit && ea.cmdidx != CMD_file @@ -1531,14 +1536,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } - if (!ni && !(ea.argt & RANGE) && ea.addr_count > 0) { - /* no range allowed */ + if (!ni && !(ea.argt & EX_RANGE) && ea.addr_count > 0) { + // no range allowed errormsg = (char_u *)_(e_norange); goto doend; } } - if (!ni && !(ea.argt & BANG) && ea.forceit) { /* no <!> allowed */ + if (!ni && !(ea.argt & EX_BANG) && ea.forceit) { // no <!> allowed errormsg = (char_u *)_(e_nobang); goto doend; } @@ -1547,12 +1552,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, * Don't complain about the range if it is not used * (could happen if line_count is accidentally set to 0). */ - if (!ea.skip && !ni) { - /* - * If the range is backwards, ask for confirmation and, if given, swap - * ea.line1 & ea.line2 so it's forwards again. - * When global command is busy, don't ask, will fail below. - */ + if (!ea.skip && !ni && (ea.argt & EX_RANGE)) { + // If the range is backwards, ask for confirmation and, if given, swap + // ea.line1 & ea.line2 so it's forwards again. + // When global command is busy, don't ask, will fail below. if (!global_busy && ea.line1 > ea.line2) { if (msg_silent == 0) { if ((flags & DOCMD_VERBOSE) || exmode_active) { @@ -1571,12 +1574,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } - if ((ea.argt & NOTADR) && ea.addr_count == 0) /* default is 1, not cursor */ + if ((ea.addr_type == ADDR_OTHER) && ea.addr_count == 0) { + // default is 1, not cursor ea.line2 = 1; + } correct_range(&ea); - if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy + if (((ea.argt & EX_WHOLEFOLD) || ea.addr_count >= 2) && !global_busy && ea.addr_type == ADDR_LINES) { // Put the first line at the start of a closed fold, put the last line // at the end of a closed fold. @@ -1610,12 +1615,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, * Check for "++opt=val" argument. * Must be first, allow ":w ++enc=utf8 !cmd" */ - if (ea.argt & ARGOPT) - while (ea.arg[0] == '+' && ea.arg[1] == '+') + if (ea.argt & EX_ARGOPT) { + while (ea.arg[0] == '+' && ea.arg[1] == '+') { if (getargopt(&ea) == FAIL && !ni) { errormsg = (char_u *)_(e_invarg); goto doend; } + } + } if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { if (*ea.arg == '>') { /* append */ @@ -1654,14 +1661,15 @@ static char_u * do_one_cmd(char_u **cmdlinep, * Check for "+command" argument, before checking for next command. * Don't do this for ":read !cmd" and ":write !cmd". */ - if ((ea.argt & EDITCMD) && !ea.usefilter) + if ((ea.argt & EX_CMDARG) && !ea.usefilter) { ea.do_ecmd_cmd = getargcmd(&ea.arg); + } /* * Check for '|' to separate commands and '"' to start comments. * Don't do this for ":read !cmd" and ":write !cmd". */ - if ((ea.argt & TRLBAR) && !ea.usefilter) { + if ((ea.argt & EX_TRLBAR) && !ea.usefilter) { separate_nextcmd(&ea); } else if (ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal @@ -1688,12 +1696,13 @@ static char_u * do_one_cmd(char_u **cmdlinep, } } - if ((ea.argt & DFLALL) && ea.addr_count == 0) { + if ((ea.argt & EX_DFLALL) && ea.addr_count == 0) { buf_T *buf; ea.line1 = 1; switch (ea.addr_type) { case ADDR_LINES: + case ADDR_OTHER: ea.line2 = curbuf->b_ml.ml_line_count; break; case ADDR_LOADED_BUFFERS: @@ -1728,21 +1737,27 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = ARGCOUNT; } break; - case ADDR_QUICKFIX: - ea.line2 = qf_get_size(&ea); + case ADDR_QUICKFIX_VALID: + ea.line2 = qf_get_valid_size(&ea); if (ea.line2 == 0) { ea.line2 = 1; } break; + case ADDR_NONE: + case ADDR_UNSIGNED: + case ADDR_QUICKFIX: + IEMSG(_("INTERNAL: Cannot use EX_DFLALL " + "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX")); + break; } } - /* accept numbered register only when no count allowed (:put) */ - if ((ea.argt & REGSTR) + // accept numbered register only when no count allowed (:put) + if ((ea.argt & EX_REGSTR) && *ea.arg != NUL /* Do not allow register = for user commands */ && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=') - && !((ea.argt & COUNT) && ascii_isdigit(*ea.arg))) { + && !((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg))) { if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put && !IS_USER_CMDIDX(ea.cmdidx)))) { ea.regname = *ea.arg++; @@ -1756,19 +1771,19 @@ static char_u * do_one_cmd(char_u **cmdlinep, } // - // Check for a count. When accepting a BUFNAME, don't use "123foo" as a + // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a // count, it's a buffer name. /// - if ((ea.argt & COUNT) && ascii_isdigit(*ea.arg) - && (!(ea.argt & BUFNAME) || *(p = skipdigits(ea.arg)) == NUL + if ((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg) + && (!(ea.argt & EX_BUFNAME) || *(p = skipdigits(ea.arg)) == NUL || ascii_iswhite(*p))) { n = getdigits_long(&ea.arg, false, -1); ea.arg = skipwhite(ea.arg); - if (n <= 0 && !ni && (ea.argt & ZEROR) == 0) { + if (n <= 0 && !ni && (ea.argt & EX_ZEROR) == 0) { errormsg = (char_u *)_(e_zerocount); goto doend; } - if (ea.argt & NOTADR) { /* e.g. :buffer 2, :sleep 3 */ + if (ea.addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3 ea.line2 = n; if (ea.addr_count == 0) ea.addr_count = 1; @@ -1777,8 +1792,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 += n - 1; ++ea.addr_count; // Be vi compatible: no error message for out of range. - if (ea.addr_type == ADDR_LINES - && ea.line2 > curbuf->b_ml.ml_line_count) { + if (ea.line2 > curbuf->b_ml.ml_line_count) { ea.line2 = curbuf->b_ml.ml_line_count; } } @@ -1787,16 +1801,17 @@ static char_u * do_one_cmd(char_u **cmdlinep, /* * Check for flags: 'l', 'p' and '#'. */ - if (ea.argt & EXFLAGS) + if (ea.argt & EX_FLAGS) { get_flags(&ea); - /* no arguments allowed */ - if (!ni && !(ea.argt & EXTRA) && *ea.arg != NUL - && *ea.arg != '"' && (*ea.arg != '|' || (ea.argt & TRLBAR) == 0)) { + } + if (!ni && !(ea.argt & EX_EXTRA) && *ea.arg != NUL + && *ea.arg != '"' && (*ea.arg != '|' || (ea.argt & EX_TRLBAR) == 0)) { + // no arguments allowed but there is something errormsg = (char_u *)_(e_trailing); goto doend; } - if (!ni && (ea.argt & NEEDARG) && *ea.arg == NUL) { + if (!ni && (ea.argt & EX_NEEDARG) && *ea.arg == NUL) { errormsg = (char_u *)_(e_argreq); goto doend; } @@ -1825,9 +1840,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, case CMD_function: break; - /* Commands that handle '|' themselves. Check: A command should - * either have the TRLBAR flag, appear in this list or appear in - * the list at ":help :bar". */ + // Commands that handle '|' themselves. Check: A command should + // either have the EX_TRLBAR flag, appear in this list or appear in + // the list at ":help :bar". case CMD_aboveleft: case CMD_and: case CMD_belowright: @@ -1898,16 +1913,17 @@ static char_u * do_one_cmd(char_u **cmdlinep, } } - if (ea.argt & XFILE) { - if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) + if (ea.argt & EX_XFILE) { + if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) { goto doend; + } } /* * Accept buffer name. Cannot be used at the same time with a buffer * number. Don't do this for a user command. */ - if ((ea.argt & BUFNAME) && *ea.arg != NUL && ea.addr_count == 0 + if ((ea.argt & EX_BUFNAME) && *ea.arg != NUL && ea.addr_count == 0 && !IS_USER_CMDIDX(ea.cmdidx) ) { /* @@ -1923,10 +1939,11 @@ static char_u * do_one_cmd(char_u **cmdlinep, while (p > ea.arg && ascii_iswhite(p[-1])) --p; } - ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & BUFUNL) != 0, - FALSE, FALSE); - if (ea.line2 < 0) /* failed */ + ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0, + false, false); + if (ea.line2 < 0) { // failed goto doend; + } ea.addr_count = 1; ea.arg = skipwhite(p); } @@ -2322,6 +2339,7 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->line1 = eap->line2; switch (eap->addr_type) { case ADDR_LINES: + case ADDR_OTHER: // default is current line number eap->line2 = curwin->w_cursor.lnum; break; @@ -2342,11 +2360,18 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->line2 = CURRENT_TAB_NR; break; case ADDR_TABS_RELATIVE: + case ADDR_UNSIGNED: eap->line2 = 1; break; case ADDR_QUICKFIX: + eap->line2 = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: eap->line2 = qf_get_cur_valid_idx(eap); break; + case ADDR_NONE: + // Will give an error later if a range is found. + break; } eap->cmd = skipwhite(eap->cmd); lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent, @@ -2359,6 +2384,7 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->cmd++; switch (eap->addr_type) { case ADDR_LINES: + case ADDR_OTHER: eap->line1 = 1; eap->line2 = curbuf->b_ml.ml_line_count; break; @@ -2394,7 +2420,8 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) } break; case ADDR_TABS_RELATIVE: - case ADDR_OTHER: + case ADDR_UNSIGNED: + case ADDR_QUICKFIX: *errormsg = (char_u *)_(e_invrange); return FAIL; case ADDR_ARGUMENTS: @@ -2405,13 +2432,16 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->line2 = ARGCOUNT; } break; - case ADDR_QUICKFIX: + case ADDR_QUICKFIX_VALID: eap->line1 = 1; - eap->line2 = qf_get_size(eap); + eap->line2 = qf_get_valid_size(eap); if (eap->line2 == 0) { eap->line2 = 1; } break; + case ADDR_NONE: + // Will give an error later if a range is found. + break; } eap->addr_count++; } else if (*eap->cmd == '*') { @@ -2513,14 +2543,13 @@ static void append_command(char_u *cmd) *d = NUL; } -/* - * Find an Ex command by its name, either built-in or user. - * Start of the name can be found at eap->cmd. - * Returns pointer to char after the command name. - * "full" is set to TRUE if the whole command name matched. - * Returns NULL for an ambiguous user command. - */ +// Find an Ex command by its name, either built-in or user. +// Start of the name can be found at eap->cmd. +// Sets eap->cmdidx and returns a pointer to char after the command name. +// "full" is set to TRUE if the whole command name matched. +// Returns NULL for an ambiguous user command. static char_u *find_command(exarg_T *eap, int *full) + FUNC_ATTR_NONNULL_ARG(1) { int len; char_u *p; @@ -2970,6 +2999,15 @@ const char * set_one_cmd_context( const char *arg = (const char *)skipwhite((const char_u *)p); + // Skip over ++argopt argument + if ((ea.argt & EX_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 == '>') { @@ -2997,9 +3035,9 @@ const char * set_one_cmd_context( arg = (const char *)skipwhite((const char_u *)arg); } - /* Does command allow "+command"? */ - if ((ea.argt & EDITCMD) && !usefilter && *arg == '+') { - /* Check if we're in the +command */ + // Does command allow "+command"? + if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') { + // Check if we're in the +command p = arg + 1; arg = (const char *)skip_cmd_arg((char_u *)arg, false); @@ -3015,17 +3053,19 @@ const char * set_one_cmd_context( * Check for '|' to separate commands and '"' to start comments. * Don't do this for ":read !cmd" and ":write !cmd". */ - if ((ea.argt & TRLBAR) && !usefilter) { + if ((ea.argt & EX_TRLBAR) && !usefilter) { p = arg; /* ":redir @" is not the start of a comment */ if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') p += 2; while (*p) { if (*p == Ctrl_V) { - if (p[1] != NUL) - ++p; - } else if ( (*p == '"' && !(ea.argt & NOTRLCOM)) - || *p == '|' || *p == '\n') { + if (p[1] != NUL) { + p++; + } + } else if ((*p == '"' && !(ea.argt & EX_NOTRLCOM)) + || *p == '|' + || *p == '\n') { if (*(p - 1) != '\\') { if (*p == '|' || *p == '\n') return p + 1; @@ -3036,8 +3076,8 @@ const char * set_one_cmd_context( } } - // no arguments allowed - if (!(ea.argt & EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) { + if (!(ea.argt & EX_EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) { + // no arguments allowed but there is something return NULL; } @@ -3057,7 +3097,7 @@ const char * set_one_cmd_context( } } - if (ea.argt & XFILE) { + if (ea.argt & EX_XFILE) { int c; int in_quote = false; const char *bow = NULL; // Beginning of word. @@ -3478,8 +3518,8 @@ const char * set_one_cmd_context( case CMD_USER: case CMD_USER_BUF: if (context != EXPAND_NOTHING) { - // XFILE: file names are handled above. - if (!(ea.argt & XFILE)) { + // EX_XFILE: file names are handled above. + if (!(ea.argt & EX_XFILE)) { if (context == EXPAND_MENUS) { return (const char *)set_context_in_menu_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit); @@ -3689,6 +3729,15 @@ char_u *skip_range( return (char_u *)cmd; } +static void addr_error(cmd_addr_T addr_type) +{ + if (addr_type == ADDR_NONE) { + EMSG(_(e_norange)); + } else { + EMSG(_(e_invrange)); + } +} + // Get a single EX address // // Set ptr to the next character after the part that was interpreted. @@ -3698,11 +3747,12 @@ char_u *skip_range( // Return MAXLNUM when no Ex address was found. static linenr_T get_address(exarg_T *eap, char_u **ptr, - int addr_type, // flag: one of ADDR_LINES, ... + cmd_addr_T addr_type, int skip, // only skip the address, don't use it bool silent, // no errors or side effects int to_other_file, // flag: may jump to other file int address_count) // 1 for first, >1 after comma + FUNC_ATTR_NONNULL_ALL { int c; int i; @@ -3721,6 +3771,7 @@ static linenr_T get_address(exarg_T *eap, ++cmd; switch (addr_type) { case ADDR_LINES: + case ADDR_OTHER: lnum = curwin->w_cursor.lnum; break; case ADDR_WINDOWS: @@ -3736,12 +3787,17 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_NONE: case ADDR_TABS_RELATIVE: - EMSG(_(e_invrange)); + case ADDR_UNSIGNED: + addr_error(addr_type); cmd = NULL; goto error; break; case ADDR_QUICKFIX: + lnum = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: lnum = qf_get_cur_valid_idx(eap); break; } @@ -3751,6 +3807,7 @@ static linenr_T get_address(exarg_T *eap, ++cmd; switch (addr_type) { case ADDR_LINES: + case ADDR_OTHER: lnum = curbuf->b_ml.ml_line_count; break; case ADDR_WINDOWS: @@ -3775,8 +3832,10 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = LAST_TAB_NR; break; + case ADDR_NONE: case ADDR_TABS_RELATIVE: - EMSG(_(e_invrange)); + case ADDR_UNSIGNED: + addr_error(addr_type); cmd = NULL; goto error; break; @@ -3786,6 +3845,12 @@ static linenr_T get_address(exarg_T *eap, lnum = 1; } break; + case ADDR_QUICKFIX_VALID: + lnum = qf_get_valid_size(eap); + if (lnum == 0) { + lnum = 1; + } + break; } break; @@ -3795,7 +3860,7 @@ static linenr_T get_address(exarg_T *eap, goto error; } if (addr_type != ADDR_LINES) { - EMSG(_(e_invaddr)); + addr_error(addr_type); cmd = NULL; goto error; } @@ -3823,7 +3888,7 @@ static linenr_T get_address(exarg_T *eap, case '?': /* '/' or '?' - search */ c = *cmd++; if (addr_type != ADDR_LINES) { - EMSG(_(e_invaddr)); + addr_error(addr_type); cmd = NULL; goto error; } @@ -3870,7 +3935,7 @@ static linenr_T get_address(exarg_T *eap, case '\\': /* "\?", "\/" or "\&", repeat search */ ++cmd; if (addr_type != ADDR_LINES) { - EMSG(_(e_invaddr)); + addr_error(addr_type); cmd = NULL; goto error; } @@ -3917,7 +3982,9 @@ static linenr_T get_address(exarg_T *eap, if (lnum == MAXLNUM) { switch (addr_type) { case ADDR_LINES: - lnum = curwin->w_cursor.lnum; /* "+1" is same as ".+1" */ + case ADDR_OTHER: + // "+1" is same as ".+1" + lnum = curwin->w_cursor.lnum; break; case ADDR_WINDOWS: lnum = CURRENT_WIN_NR; @@ -3936,8 +4003,15 @@ static linenr_T get_address(exarg_T *eap, lnum = 1; break; case ADDR_QUICKFIX: + lnum = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: lnum = qf_get_cur_valid_idx(eap); break; + case ADDR_NONE: + case ADDR_UNSIGNED: + lnum = 0; + break; } } @@ -4027,12 +4101,11 @@ static char_u *invalid_range(exarg_T *eap) return (char_u *)_(e_invrange); } - if (eap->argt & RANGE) { - switch(eap->addr_type) { + if (eap->argt & EX_RANGE) { + switch (eap->addr_type) { case ADDR_LINES: - if (!(eap->argt & NOTADR) - && eap->line2 > curbuf->b_ml.ml_line_count - + (eap->cmdidx == CMD_diffget)) { + if (eap->line2 > (curbuf->b_ml.ml_line_count + + (eap->cmdidx == CMD_diffget))) { return (char_u *)_(e_invrange); } break; @@ -4081,14 +4154,30 @@ static char_u *invalid_range(exarg_T *eap) } break; case ADDR_TABS_RELATIVE: - // Do nothing + case ADDR_OTHER: + // Any range is OK. break; case ADDR_QUICKFIX: assert(eap->line2 >= 0); - if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) { + // No error for value that is too big, will use the last entry. + if (eap->line2 <= 0) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_QUICKFIX_VALID: + if ((eap->line2 != 1 && (size_t)eap->line2 > qf_get_valid_size(eap)) + || eap->line2 < 0) { return (char_u *)_(e_invrange); } break; + case ADDR_UNSIGNED: + if (eap->line2 < 0) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_NONE: + // Will give an error elsewhere. + break; } } return NULL; @@ -4099,11 +4188,13 @@ static char_u *invalid_range(exarg_T *eap) */ static void correct_range(exarg_T *eap) { - if (!(eap->argt & ZEROR)) { /* zero in range not allowed */ - if (eap->line1 == 0) + if (!(eap->argt & EX_ZEROR)) { // zero in range not allowed + if (eap->line1 == 0) { eap->line1 = 1; - if (eap->line2 == 0) + } + if (eap->line2 == 0) { eap->line2 = 1; + } } } @@ -4270,7 +4361,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) && eap->cmdidx != CMD_lmake && eap->cmdidx != CMD_make && eap->cmdidx != CMD_terminal - && !(eap->argt & NOSPC) + && !(eap->argt & EX_NOSPC) ) { char_u *l; #ifdef BACKSLASH_IN_FILENAME @@ -4311,7 +4402,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) * One file argument: Expand wildcards. * Don't do this with ":r !command" or ":w !command". */ - if ((eap->argt & NOSPC) && !eap->usefilter) { + if ((eap->argt & EX_NOSPC) && !eap->usefilter) { // Replace environment variables. if (has_wildcards) { /* @@ -4424,38 +4515,39 @@ void separate_nextcmd(exarg_T *eap) for (; *p; MB_PTR_ADV(p)) { if (*p == Ctrl_V) { - if (eap->argt & (USECTRLV | XFILE)) - ++p; /* skip CTRL-V and next char */ - else - /* remove CTRL-V and skip next char */ + if (eap->argt & (EX_CTRLV | EX_XFILE)) { + p++; // skip CTRL-V and next char + } else { + // remove CTRL-V and skip next char STRMOVE(p, p + 1); - if (*p == NUL) /* stop at NUL after CTRL-V */ + } + if (*p == NUL) { // stop at NUL after CTRL-V break; - } - /* Skip over `=expr` when wildcards are expanded. */ - else if (p[0] == '`' && p[1] == '=' && (eap->argt & XFILE)) { + } + } else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) { + // Skip over `=expr` when wildcards are expanded. p += 2; (void)skip_expr(&p); if (*p == NUL) { // stop at NUL after CTRL-V break; } - } - /* Check for '"': start of comment or '|': next command */ - /* :@" does not start a comment! - * :redir @" doesn't either. */ - else if ((*p == '"' && !(eap->argt & NOTRLCOM) - && (eap->cmdidx != CMD_at || p != eap->arg) - && (eap->cmdidx != CMD_redir - || p != eap->arg + 1 || p[-1] != '@')) - || *p == '|' || *p == '\n') { - /* - * We remove the '\' before the '|', unless USECTRLV is used - * AND 'b' is present in 'cpoptions'. - */ + } else if ( + // Check for '"': start of comment or '|': next command */ + // :@" does not start a comment! + // :redir @" doesn't either. + (*p == '"' + && !(eap->argt & EX_NOTRLCOM) + && (eap->cmdidx != CMD_at || p != eap->arg) + && (eap->cmdidx != CMD_redir + || p != eap->arg + 1 || p[-1] != '@')) + || *p == '|' + || *p == '\n') { + // We remove the '\' before the '|', unless EX_CTRLV is used + // AND 'b' is present in 'cpoptions'. if ((vim_strchr(p_cpo, CPO_BAR) == NULL - || !(eap->argt & USECTRLV)) && *(p - 1) == '\\') { - STRMOVE(p - 1, p); /* remove the '\' */ - --p; + || !(eap->argt & EX_CTRLV)) && *(p - 1) == '\\') { + STRMOVE(p - 1, p); // remove the '\' + p--; } else { eap->nextcmd = check_nextcmd(p); *p = NUL; @@ -4464,8 +4556,9 @@ void separate_nextcmd(exarg_T *eap) } } - if (!(eap->argt & NOTRLCOM)) /* remove trailing spaces */ + if (!(eap->argt & EX_NOTRLCOM)) { // remove trailing spaces del_trailing_spaces(eap->arg); + } } /* @@ -4742,10 +4835,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; @@ -4957,10 +5048,10 @@ char_u *get_command_name(expand_T *xp, int idx) static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, int compl, - char_u *compl_arg, int addr_type, int force) + char_u *compl_arg, cmd_addr_T addr_type, bool force) + FUNC_ATTR_NONNULL_ARG(1, 3) { ucmd_T *cmd = NULL; - char_u *p; int i; int cmp = 1; char_u *rep_buf = NULL; @@ -5020,7 +5111,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)); @@ -5049,7 +5140,7 @@ fail: static struct { - int expand; + cmd_addr_T expand; char *name; char *shortname; } addr_type_complete[] = @@ -5062,7 +5153,7 @@ static struct { { ADDR_WINDOWS, "windows", "win" }, { ADDR_QUICKFIX, "quickfix", "qf" }, { ADDR_OTHER, "other", "?" }, - { -1, NULL, NULL } + { ADDR_NONE, NULL, NULL } }; /* @@ -5156,11 +5247,11 @@ static void uc_list(char_u *name, size_t name_len) // Special cases int len = 4; - if (a & BANG) { + if (a & EX_BANG) { msg_putchar('!'); len--; } - if (a & REGSTR) { + if (a & EX_REGSTR) { msg_putchar('"'); len--; } @@ -5168,7 +5259,7 @@ static void uc_list(char_u *name, size_t name_len) msg_putchar('b'); len--; } - if (a & TRLBAR) { + if (a & EX_TRLBAR) { msg_putchar('|'); len--; } @@ -5190,20 +5281,20 @@ static void uc_list(char_u *name, size_t name_len) len = 0; // Arguments - switch (a & (EXTRA|NOSPC|NEEDARG)) { + switch (a & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { case 0: IObuff[len++] = '0'; break; - case (EXTRA): + case (EX_EXTRA): IObuff[len++] = '*'; break; - case (EXTRA|NOSPC): + case (EX_EXTRA | EX_NOSPC): IObuff[len++] = '?'; break; - case (EXTRA|NEEDARG): + case (EX_EXTRA | EX_NEEDARG): IObuff[len++] = '+'; break; - case (EXTRA|NOSPC|NEEDARG): + case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): IObuff[len++] = '1'; break; } @@ -5213,13 +5304,13 @@ static void uc_list(char_u *name, size_t name_len) } while (len < 5 - over); // Address / Range - if (a & (RANGE|COUNT)) { - if (a & COUNT) { + if (a & (EX_RANGE | EX_COUNT)) { + if (a & EX_COUNT) { // -count=N snprintf((char *)IObuff + len, IOSIZE, "%" PRId64 "c", (int64_t)cmd->uc_def); len += (int)STRLEN(IObuff + len); - } else if (a & DFLALL) { + } else if (a & EX_DFLALL) { IObuff[len++] = '%'; } else if (cmd->uc_def >= 0) { // -range=N @@ -5236,7 +5327,7 @@ static void uc_list(char_u *name, size_t name_len) } while (len < 8 - over); // Address Type - for (j = 0; addr_type_complete[j].expand != -1; j++) { + for (j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { STRCPY(IObuff + len, addr_type_complete[j].shortname); @@ -5284,7 +5375,8 @@ static void uc_list(char_u *name, size_t name_len) static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags, int *complp, char_u **compl_arg, - int *addr_type_arg) + cmd_addr_T *addr_type_arg) + FUNC_ATTR_NONNULL_ALL { char_u *p; @@ -5293,16 +5385,16 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, return FAIL; } - /* First, try the simple attributes (no arguments) */ - if (STRNICMP(attr, "bang", len) == 0) - *argt |= BANG; - else if (STRNICMP(attr, "buffer", len) == 0) + // First, try the simple attributes (no arguments) + if (STRNICMP(attr, "bang", len) == 0) { + *argt |= EX_BANG; + } else if (STRNICMP(attr, "buffer", len) == 0) { *flags |= UC_BUFFER; - else if (STRNICMP(attr, "register", len) == 0) - *argt |= REGSTR; - else if (STRNICMP(attr, "bar", len) == 0) - *argt |= TRLBAR; - else { + } else if (STRNICMP(attr, "register", len) == 0) { + *argt |= EX_REGSTR; + } else if (STRNICMP(attr, "bar", len) == 0) { + *argt |= EX_TRLBAR; + } else { int i; char_u *val = NULL; size_t vallen = 0; @@ -5320,28 +5412,29 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, if (STRNICMP(attr, "nargs", attrlen) == 0) { if (vallen == 1) { - if (*val == '0') - /* Do nothing - this is the default */; - else if (*val == '1') - *argt |= (EXTRA | NOSPC | NEEDARG); - else if (*val == '*') - *argt |= EXTRA; - else if (*val == '?') - *argt |= (EXTRA | NOSPC); - else if (*val == '+') - *argt |= (EXTRA | NEEDARG); - else + if (*val == '0') { + // Do nothing - this is the default; + } else if (*val == '1') { + *argt |= (EX_EXTRA | EX_NOSPC | EX_NEEDARG); + } else if (*val == '*') { + *argt |= EX_EXTRA; + } else if (*val == '?') { + *argt |= (EX_EXTRA | EX_NOSPC); + } else if (*val == '+') { + *argt |= (EX_EXTRA | EX_NEEDARG); + } else { goto wrong_nargs; + } } else { wrong_nargs: EMSG(_("E176: Invalid number of arguments")); return FAIL; } } else if (STRNICMP(attr, "range", attrlen) == 0) { - *argt |= RANGE; - if (vallen == 1 && *val == '%') - *argt |= DFLALL; - else if (val != NULL) { + *argt |= EX_RANGE; + if (vallen == 1 && *val == '%') { + *argt |= EX_DFLALL; + } else if (val != NULL) { p = val; if (*def >= 0) { two_count: @@ -5350,7 +5443,7 @@ two_count: } *def = getdigits_long(&p, true, 0); - *argt |= (ZEROR | NOTADR); + *argt |= EX_ZEROR; if (p != val + vallen || vallen == 0) { invalid_count: @@ -5358,8 +5451,16 @@ invalid_count: return FAIL; } } + // default for -range is using buffer lines + if (*addr_type_arg == ADDR_NONE) { + *addr_type_arg = ADDR_LINES; + } } else if (STRNICMP(attr, "count", attrlen) == 0) { - *argt |= (COUNT | ZEROR | RANGE | NOTADR); + *argt |= (EX_COUNT | EX_ZEROR | EX_RANGE); + // default for -count is using any number + if (*addr_type_arg == ADDR_NONE) { + *addr_type_arg = ADDR_OTHER; + } if (val != NULL) { p = val; @@ -5385,16 +5486,16 @@ invalid_count: return FAIL; } } else if (STRNICMP(attr, "addr", attrlen) == 0) { - *argt |= RANGE; + *argt |= EX_RANGE; if (val == NULL) { EMSG(_("E179: argument required for -addr")); return FAIL; } - if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) == FAIL) { + if (parse_addr_type_arg(val, (int)vallen, addr_type_arg) == FAIL) { return FAIL; } if (*addr_type_arg != ADDR_LINES) { - *argt |= (ZEROR | NOTADR); + *argt |= EX_ZEROR; } } else { char_u ch = attr[len]; @@ -5421,7 +5522,7 @@ static void ex_command(exarg_T *eap) int flags = 0; int compl = EXPAND_NOTHING; char_u *compl_arg = NULL; - int addr_type_arg = ADDR_LINES; + cmd_addr_T addr_type_arg = ADDR_NONE; int has_attr = (eap->arg[0] == '-'); int name_len; @@ -5698,8 +5799,9 @@ uc_check_code( /* When specified there is a single argument don't split it. * Works for ":Cmd %" when % is "a b c". */ - if ((eap->argt & NOSPC) && quote == 2) + if ((eap->argt & EX_NOSPC) && quote == 2) { quote = 1; + } switch (quote) { case 0: /* No quoting, no splitting */ @@ -6079,12 +6181,12 @@ char_u *get_user_cmd_complete(expand_T *xp, int idx) /* * Parse address type argument */ -int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, - int *addr_type_arg) +int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg) + FUNC_ATTR_NONNULL_ALL { int i, a, b; - for (i = 0; addr_type_complete[i].expand != -1; i++) { + for (i = 0; addr_type_complete[i].expand != ADDR_NONE; i++) { a = (int)STRLEN(addr_type_complete[i].name) == vallen; b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; if (a && b) { @@ -6093,7 +6195,7 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, } } - if (addr_type_complete[i].expand == -1) { + if (addr_type_complete[i].expand == ADDR_NONE) { char_u *err = value; for (i = 0; err[i] != NUL && !ascii_iswhite(err[i]); i++) {} @@ -6102,9 +6204,6 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, return FAIL; } - if (*addr_type_arg != ADDR_LINES) - *argt |= NOTADR; - return OK; } @@ -6117,6 +6216,7 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, */ int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt, char_u **compl_arg) + FUNC_ATTR_NONNULL_ALL { const char_u *arg = NULL; size_t arglen = 0; @@ -6141,9 +6241,9 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, && STRNCMP(value, command_complete[i], valend) == 0) { *complp = i; if (i == EXPAND_BUFFERS) { - *argt |= BUFNAME; + *argt |= EX_BUFNAME; } else if (i == EXPAND_DIRECTORIES || i == EXPAND_FILES) { - *argt |= XFILE; + *argt |= EX_XFILE; } break; } @@ -6166,8 +6266,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; } @@ -7340,7 +7441,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; } @@ -7394,11 +7495,12 @@ static void ex_read(exarg_T *eap) int empty = (curbuf->b_ml.ml_flags & ML_EMPTY); linenr_T lnum; - if (eap->usefilter) /* :r!cmd */ - do_bang(1, eap, FALSE, FALSE, TRUE); - else { - if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) + if (eap->usefilter) { // :r!cmd + do_bang(1, eap, false, false, true); + } else { + if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) { return; + } if (*eap->arg == NUL) { if (check_fname() == FAIL) /* check for no file name */ @@ -7770,7 +7872,7 @@ static void ex_copymove(exarg_T *eap) * move or copy lines from 'eap->line1'-'eap->line2' to below line 'n' */ if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count) { - EMSG(_(e_invaddr)); + EMSG(_(e_invrange)); return; } @@ -7868,7 +7970,7 @@ static void ex_at(exarg_T *eap) */ static void ex_bang(exarg_T *eap) { - do_bang(eap->addr_count, eap, eap->forceit, TRUE, TRUE); + do_bang(eap->addr_count, eap, eap->forceit, true, true); } /* @@ -8028,7 +8130,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); } @@ -8177,10 +8279,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(); } @@ -8617,6 +8720,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen) * '#' to curwin->w_altfile * '<cword>' to word under the cursor * '<cWORD>' to WORD under the cursor + * '<cexpr>' to C-expression under the cursor * '<cfile>' to path name under the cursor * '<sfile>' to sourced file name * '<slnum>' to sourced file line number @@ -9260,7 +9364,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) { @@ -9445,16 +9549,16 @@ Dictionary commands_array(buf_T *buf) PUT(d, "name", STRING_OBJ(cstr_to_string((char *)cmd->uc_name))); PUT(d, "definition", STRING_OBJ(cstr_to_string((char *)cmd->uc_rep))); PUT(d, "script_id", INTEGER_OBJ(cmd->uc_script_ctx.sc_sid)); - PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & BANG))); - PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & TRLBAR))); - PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & REGSTR))); + PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); + PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); + PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); - switch (cmd->uc_argt & (EXTRA|NOSPC|NEEDARG)) { - case 0: arg[0] = '0'; break; - case(EXTRA): arg[0] = '*'; break; - case(EXTRA|NOSPC): arg[0] = '?'; break; - case(EXTRA|NEEDARG): arg[0] = '+'; break; - case(EXTRA|NOSPC|NEEDARG): arg[0] = '1'; break; + switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { + case 0: arg[0] = '0'; break; + case(EX_EXTRA): arg[0] = '*'; break; + case(EX_EXTRA | EX_NOSPC): arg[0] = '?'; break; + case(EX_EXTRA | EX_NEEDARG): arg[0] = '+'; break; + case(EX_EXTRA | EX_NOSPC | EX_NEEDARG): arg[0] = '1'; break; } PUT(d, "nargs", STRING_OBJ(cstr_to_string(arg))); @@ -9465,7 +9569,7 @@ Dictionary commands_array(buf_T *buf) ? NIL : STRING_OBJ(cstr_to_string((char *)cmd->uc_compl_arg))); Object obj = NIL; - if (cmd->uc_argt & COUNT) { + if (cmd->uc_argt & EX_COUNT) { if (cmd->uc_def >= 0) { snprintf(str, sizeof(str), "%" PRId64, (int64_t)cmd->uc_def); obj = STRING_OBJ(cstr_to_string(str)); // -count=N @@ -9476,8 +9580,8 @@ Dictionary commands_array(buf_T *buf) PUT(d, "count", obj); obj = NIL; - if (cmd->uc_argt & RANGE) { - if (cmd->uc_argt & DFLALL) { + if (cmd->uc_argt & EX_RANGE) { + if (cmd->uc_argt & EX_DFLALL) { obj = STRING_OBJ(cstr_to_string("%")); // -range=% } else if (cmd->uc_def >= 0) { snprintf(str, sizeof(str), "%" PRId64, (int64_t)cmd->uc_def); @@ -9489,7 +9593,7 @@ Dictionary commands_array(buf_T *buf) PUT(d, "range", obj); obj = NIL; - for (int j = 0; addr_type_complete[j].expand != -1; j++) { + for (int j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { obj = STRING_OBJ(cstr_to_string(addr_type_complete[j].name)); diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 0c7562980a..0917c6dd02 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -518,10 +518,13 @@ fail: * Discard an exception. "was_finished" is set when the exception has been * caught and the catch clause has been ended normally. */ -static void discard_exception(except_T *excp, int was_finished) +static void discard_exception(except_T *excp, bool was_finished) { char_u *saved_IObuff; + if (current_exception == excp) { + current_exception = NULL; + } if (excp == NULL) { internal_error("discard_exception()"); return; @@ -569,7 +572,6 @@ void discard_current_exception(void) { if (current_exception != NULL) { discard_exception(current_exception, false); - current_exception = NULL; } // Note: all globals manipulated here should be saved/restored in // try_enter/try_leave. @@ -652,8 +654,8 @@ static void finish_exception(except_T *excp) set_vim_var_string(VV_THROWPOINT, NULL, -1); } - /* Discard the exception, but use the finish message for 'verbose'. */ - discard_exception(excp, TRUE); + // Discard the exception, but use the finish message for 'verbose'. + discard_exception(excp, true); } /* @@ -1812,11 +1814,12 @@ void leave_cleanup(cleanup_T *csp) * made pending by it. Report this to the user if required by the * 'verbose' option or when debugging. */ if (aborting() || need_rethrow) { - if (pending & CSTP_THROW) - /* Cancel the pending exception (includes report). */ - discard_exception(csp->exception, FALSE); - else + if (pending & CSTP_THROW) { + // Cancel the pending exception (includes report). + discard_exception(csp->exception, false); + } else { report_discard_pending(pending, NULL); + } /* If an error was about to be converted to an exception when * enter_cleanup() was called, free the message list. */ @@ -1916,15 +1919,13 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) default: if (cstack->cs_flags[idx] & CSF_FINALLY) { if (cstack->cs_pending[idx] & CSTP_THROW) { - /* Cancel the pending exception. This is in the - * finally clause, so that the stack of the - * caught exceptions is not involved. */ - discard_exception((except_T *) - cstack->cs_exception[idx], - FALSE); - } else - report_discard_pending(cstack->cs_pending[idx], - NULL); + // Cancel the pending exception. This is in the + // finally clause, so that the stack of the + // caught exceptions is not involved. + discard_exception((except_T *)cstack->cs_exception[idx], false); + } else { + report_discard_pending(cstack->cs_pending[idx], NULL); + } cstack->cs_pending[idx] = CSTP_NONE; } break; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index ed408c28e5..626b840798 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); @@ -1880,9 +1884,6 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); // Ignore mouse case K_MIDDLEMOUSE: - if (!mouse_has(MOUSE_COMMAND)) { - return command_line_not_changed(s); // Ignore mouse - } cmdline_paste(eval_has_provider("clipboard") ? '*' : 0, true, true); redrawcmd(); return command_line_changed(s); @@ -1906,10 +1907,6 @@ static int command_line_handle_key(CommandLineState *s) s->ignore_drag_release = false; } - if (!mouse_has(MOUSE_COMMAND)) { - return command_line_not_changed(s); // Ignore mouse - } - ccline.cmdspos = cmd_startcol(); for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen; ccline.cmdpos++) { @@ -2242,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(); @@ -2251,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)) { @@ -4229,17 +4228,11 @@ ExpandOne ( * Prepare an expand structure for use. */ void ExpandInit(expand_T *xp) + FUNC_ATTR_NONNULL_ALL { - xp->xp_pattern = NULL; - xp->xp_pattern_len = 0; + CLEAR_POINTER(xp); xp->xp_backslash = XP_BS_NONE; -#ifndef BACKSLASH_IN_FILENAME - xp->xp_shell = FALSE; -#endif xp->xp_numfiles = -1; - xp->xp_files = NULL; - xp->xp_arg = NULL; - xp->xp_line = NULL; } /* @@ -4327,7 +4320,8 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o /// if true then it escapes for a shell command. /// /// @return [allocated] escaped file name. -char *vim_strsave_fnameescape(const char *const fname, const bool shell) +char *vim_strsave_fnameescape(const char *const fname, + const bool shell FUNC_ATTR_UNUSED) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { #ifdef BACKSLASH_IN_FILENAME @@ -4786,7 +4780,7 @@ char_u *addstar(char_u *fname, size_t len, int context) * EXPAND_COMMANDS Cursor is still touching the command, so complete * it. * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands. - * EXPAND_FILES After command with XFILE set, or after setting + * EXPAND_FILES After command with EX_XFILE set, or after setting * with P_EXPAND set. eg :e ^I, :w>>^I * EXPAND_DIRECTORIES In some cases this is used instead of the latter * when we know only directories are of interest. eg @@ -5398,7 +5392,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, } /// Call "user_expand_func()" to invoke a user defined Vim script function and -/// return the result (either a string or a List). +/// return the result (either a string, a List or NULL). static void * call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file, char_u ***file) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 42a9ef08f9..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" @@ -893,6 +915,9 @@ void ex_mkrc(exarg_T *eap) && (*flagp & SSOP_OPTIONS))) { failed |= (makemap(fd, NULL) == FAIL || makeset(fd, OPT_GLOBAL, false) == FAIL); + if (p_hls && fprintf(fd, "%s", "set hlsearch\n") < 0) { + failed = true; + } } if (!failed && view_session) { @@ -949,11 +974,16 @@ void ex_mkrc(exarg_T *eap) } if (fprintf(fd, "%s", - "let &g:so = s:so_save | let &g:siso = s:siso_save\n" - "doautoall SessionLoadPost\n") + "let &g:so = s:so_save | let &g:siso = s:siso_save\n") < 0) { failed = true; } + if (no_hlsearch && fprintf(fd, "%s", "nohlsearch\n") < 0) { + failed = true; + } + if (fprintf(fd, "%s", "doautoall SessionLoadPost\n") < 0) { + failed = true; + } if (eap->cmdidx == CMD_mksession) { if (fprintf(fd, "unlet SessionLoad\n") < 0) { failed = true; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index ba685b158e..b2d8532cd7 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -560,6 +560,23 @@ void extmark_adjust(buf_T *buf, new_row, 0, new_byte, undo); } +// Adjust extmarks following a text edit. +// +// @param buf +// @param start_row Start row of the region to be changed +// @param start_col Start col of the region to be changed +// @param old_row End row of the region to be changed. +// Encoded as an offset to start_row. +// @param old_col End col of the region to be changed. Encodes +// an offset from start_col if old_row = 0; otherwise, +// encodes the end column of the old region. +// @param old_byte Byte extent of the region to be changed. +// @param new_row Row offset of the new region. +// @param new_col Col offset of the new region. Encodes an offset from +// start_col if new_row = 0; otherwise, encodes +// the end column of the new region. +// @param new_byte Byte extent of the new region. +// @param undo void extmark_splice(buf_T *buf, int start_row, colnr_T start_col, int old_row, colnr_T old_col, bcount_t old_byte, diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index ab8072ae1d..a542bb19dd 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -15,6 +15,7 @@ #include "nvim/ascii.h" #include "nvim/fileio.h" #include "nvim/buffer.h" +#include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -1651,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 @@ -2031,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); @@ -4216,7 +4214,9 @@ void shorten_buf_fname(buf_T *buf, char_u *dirname, int force) && (force || buf->b_sfname == NULL || path_is_absolute(buf->b_sfname))) { - XFREE_CLEAR(buf->b_sfname); + if (buf->b_sfname != buf->b_ffname) { + XFREE_CLEAR(buf->b_sfname); + } p = path_shorten_fname(buf->b_ffname, dirname); if (p != NULL) { buf->b_sfname = vim_strsave(p); @@ -4672,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. */ @@ -4700,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; } @@ -4770,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; @@ -5082,6 +5077,7 @@ void buf_reload(buf_T *buf, int orig_mode) // Mark all undo states as changed. u_unchanged(curbuf); } + buf_updates_unregister_all(curbuf); } } xfree(ea.cmd); @@ -5101,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/fold.c b/src/nvim/fold.c index 654aa6d5ba..0593c16999 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -902,8 +902,9 @@ int foldMoveTo( bool last = false; for (;; ) { if (!foldFind(gap, curwin->w_cursor.lnum - lnum_off, &fp)) { - if (!updown) + if (!updown || gap->ga_len == 0) { break; + } /* When moving up, consider a fold above the cursor; when * moving down consider a fold below the cursor. */ diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index 38b51b5564..e0b0610646 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -213,6 +213,8 @@ # define FUNC_API_REMOTE_ONLY /// API function not exposed in VimL/remote. # define FUNC_API_LUA_ONLY +/// API function checked textlock. +# define FUNC_API_CHECK_TEXTLOCK /// API function introduced at the given API level. # define FUNC_API_SINCE(X) /// API function deprecated since the given API level. diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua index de098b7a7b..c9ab0cf709 100644 --- a/src/nvim/generators/c_grammar.lua +++ b/src/nvim/generators/c_grammar.lua @@ -43,6 +43,7 @@ local c_proto = Ct( (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) * (fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) * (fill * Cg((P('FUNC_API_LUA_ONLY') * Cc(true)), 'lua_only') ^ -1) * + (fill * Cg((P('FUNC_API_CHECK_TEXTLOCK') * Cc(true)), 'check_textlock') ^ -1) * (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) * (fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) * (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) * diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index b31209e8ff..7e78b9e9d6 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -260,6 +260,13 @@ for i = 1, #functions do args[#args + 1] = converted end + if fn.check_textlock then + output:write('\n if (textlock != 0) {') + output:write('\n api_set_error(error, kErrorTypeException, "%s", e_secure);') + output:write('\n goto cleanup;') + output:write('\n }\n') + end + -- function call local call_args = table.concat(args, ', ') output:write('\n ') @@ -393,6 +400,16 @@ local function process_function(fn) } ]], fn.name)) end + + if fn.check_textlock then + write_shifted_output(output, [[ + if (textlock != 0) { + api_set_error(&err, kErrorTypeException, "%s", e_secure); + goto exit_0; + } + ]]) + end + local cparams = '' local free_code = {} for j = #fn.parameters,1,-1 do diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua index 849c82f50e..844661adc3 100644 --- a/src/nvim/generators/gen_ex_cmds.lua +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -22,7 +22,10 @@ local defsfname = autodir .. '/ex_cmds_defs.generated.h' local enumfile = io.open(enumfname, 'w') local defsfile = io.open(defsfname, 'w') -local defs = require('ex_cmds') +local bit = require 'bit' +local ex_cmds = require('ex_cmds') +local defs = ex_cmds.cmds +local flags = ex_cmds.flags local byte_a = string.byte('a') local byte_z = string.byte('z') @@ -51,6 +54,17 @@ static CommandDefinition cmdnames[%u] = { ]], #defs, #defs)) local cmds, cmdidxs1, cmdidxs2 = {}, {}, {} for _, cmd in ipairs(defs) do + if bit.band(cmd.flags, flags.RANGE) == flags.RANGE then + assert(cmd.addr_type ~= 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Using RANGE with ADDR_NONE\n', cmd.command)) + else + assert(cmd.addr_type == 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Missing ADDR_NONE\n', cmd.command)) + end + if bit.band(cmd.flags, flags.DFLALL) == flags.DFLALL then + assert(cmd.addr_type ~= 'ADDR_OTHER' and cmd.addr_type ~= 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Missing misplaced DFLALL\n', cmd.command)) + end local enumname = cmd.enum or ('CMD_' .. cmd.command) local byte_cmd = cmd.command:sub(1, 1):byte() if byte_a <= byte_cmd and byte_cmd <= byte_z then @@ -62,7 +76,7 @@ for _, cmd in ipairs(defs) do .cmd_name = (char_u *) "%s", .cmd_func = (ex_func_T)&%s, .cmd_argt = %uL, - .cmd_addr_type = %i + .cmd_addr_type = %s }, ]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type)) end diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 566a5ae9d6..3b8f4116b7 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 @@ -864,7 +866,6 @@ EXTERN char_u e_failed[] INIT(= N_("E472: Command failed")); EXTERN char_u e_internal[] INIT(= N_("E473: Internal error")); EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s")); EXTERN char_u e_interr[] INIT(= N_("Interrupted")); -EXTERN char_u e_invaddr[] INIT(= N_("E14: Invalid address")); EXTERN char_u e_invarg[] INIT(= N_("E474: Invalid argument")); EXTERN char_u e_invarg2[] INIT(= N_("E475: Invalid argument: %s")); EXTERN char_u e_invargval[] INIT(= N_("E475: Invalid value for argument %s")); @@ -935,6 +936,7 @@ EXTERN char_u e_readonly[] INIT(= N_( "E45: 'readonly' option is set (add ! to override)")); EXTERN char_u e_readonlyvar[] INIT(= N_( "E46: Cannot change read-only variable \"%.*s\"")); +EXTERN char_u e_stringreq[] INIT(= N_("E928: String required")); EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required")); EXTERN char_u e_toomanyarg[] INIT(= N_( "E118: Too many arguments for function: %s")); @@ -1052,4 +1054,7 @@ typedef enum { #define MIN_CD_SCOPE kCdScopeWindow #define MAX_CD_SCOPE kCdScopeGlobal +// Only filled for Win32. +EXTERN char windowsVersion[20] INIT(= { 0 }); + #endif // NVIM_GLOBALS_H diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 2c954008b3..4ec949759c 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -964,7 +964,6 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T #define PRT_PS_DEFAULT_DPI (72) // Default user space resolution #define PRT_PS_DEFAULT_FONTSIZE (10) -#define PRT_PS_DEFAULT_BUFFER_SIZE (80) #define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / \ sizeof(struct prt_mediasize_S)) diff --git a/src/nvim/log.c b/src/nvim/log.c index 225e40cdb4..19203a3c2a 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -13,9 +13,6 @@ #include <stdarg.h> #include <stdbool.h> #include <stdio.h> -#if !defined(WIN32) -# include <sys/time.h> // for gettimeofday() -#endif #include <uv.h> #include "auto/config.h" @@ -296,12 +293,10 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, } int millis = 0; -#if !defined(WIN32) - struct timeval curtime; - if (gettimeofday(&curtime, NULL) == 0) { + uv_timeval64_t curtime; + if (uv_gettimeofday(&curtime) == 0) { millis = (int)curtime.tv_usec / 1000; } -#endif // Print the log message. int64_t pid = os_get_pid(); diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 2c7ab46ffe..b20fbbf038 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -115,7 +115,8 @@ function vim._load_package(name) for _,path in ipairs(paths) do local found = vim.api.nvim_get_runtime_file(path, false) if #found > 0 then - return loadfile(found[1]) + local f, err = loadfile(found[1]) + return f or error(err) end end @@ -123,7 +124,8 @@ function vim._load_package(name) local path = "lua/"..trail:gsub('?',basename) local found = vim.api.nvim_get_runtime_file(path, false) if #found > 0 then - return package.loadlib(found[1]) + local f, err = package.loadlib(found[1]) + return f or error(err) end end return nil diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 07dcb4a8e8..303722b4a8 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -98,9 +98,6 @@ # define UTF_COMPOSINGLIKE(p1, p2) utf_composinglike((p1), (p2)) -// Whether to draw the vertical bar on the right side of the cell. -# define CURSOR_BAR_RIGHT (curwin->w_p_rl && (!(State & CMDLINE) || cmdmsg_rl)) - // MB_PTR_ADV(): advance a pointer to the next character, taking care of // multi-byte characters if needed. // MB_PTR_BACK(): backup a pointer to the previous character, taking care of diff --git a/src/nvim/main.c b/src/nvim/main.c index 79c165419e..8bf745966e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -169,6 +169,14 @@ void early_init(mparm_T *paramp) init_normal_cmds(); // Init the table of Normal mode commands. highlight_init(); +#ifdef WIN32 + OSVERSIONINFO ovi; + ovi.dwOSVersionInfoSize = sizeof(ovi); + GetVersionEx(&ovi); + snprintf(windowsVersion, sizeof(windowsVersion), "%d.%d", + (int)ovi.dwMajorVersion, (int)ovi.dwMinorVersion); +#endif + #if defined(HAVE_LOCALE_H) // Setup to use the current locale (for ctype() and many other things). // NOTE: Translated messages with encodings other than latin1 will not @@ -531,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"); } @@ -645,7 +653,18 @@ void getout(int exitval) } } } - apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, FALSE, curbuf); + + int unblock = 0; + // deathtrap() blocks autocommands, but we do want to trigger + // VimLeavePre. + if (is_autocmd_blocked()) { + unblock_autocmds(); + unblock++; + } + apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, false, curbuf); + if (unblock) { + block_autocmds(); + } } if (p_shada && *p_shada != NUL) { @@ -654,7 +673,17 @@ void getout(int exitval) } if (v_dying <= 1) { + int unblock = 0; + + // deathtrap() blocks autocommands, but we do want to trigger VimLeave. + if (is_autocmd_blocked()) { + unblock_autocmds(); + unblock++; + } apply_autocmds(EVENT_VIMLEAVE, NULL, NULL, false, curbuf); + if (unblock) { + block_autocmds(); + } } profile_dump(); @@ -1414,7 +1443,10 @@ static void read_stdin(void) no_wait_return = true; int save_msg_didany = msg_didany; set_buflisted(true); - (void)open_buffer(true, NULL, 0); // create memfile and read file + + // Create memfile and read from stdin. + (void)open_buffer(true, NULL, 0); + if (BUFEMPTY() && curbuf->b_next != NULL) { // stdin was empty, go to buffer 2 (e.g. "echo file1 | xargs nvim"). #8561 do_cmdline_cmd("silent! bnext"); diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 1ecfae57ed..73a9c1d1d7 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -777,6 +777,7 @@ void ex_delmarks(exarg_T *eap) n = i - 'A'; } namedfm[n].fmark.mark.lnum = 0; + namedfm[n].fmark.fnum = 0; XFREE_CLEAR(namedfm[n].fname); } } @@ -1552,3 +1553,87 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp) } } } + + +// Add information about mark 'mname' to list 'l' +static int add_mark(list_T *l, const char *mname, const pos_T *pos, int bufnr, + const char *fname) + FUNC_ATTR_NONNULL_ARG(1, 2, 3) +{ + if (pos->lnum <= 0) { + return OK; + } + + dict_T *d = tv_dict_alloc(); + tv_list_append_dict(l, d); + + list_T *lpos = tv_list_alloc(kListLenMayKnow); + + tv_list_append_number(lpos, bufnr); + tv_list_append_number(lpos, pos->lnum); + tv_list_append_number(lpos, pos->col + 1); + tv_list_append_number(lpos, pos->coladd); + + if (tv_dict_add_str(d, S_LEN("mark"), mname) == FAIL + || tv_dict_add_list(d, S_LEN("pos"), lpos) == FAIL + || (fname != NULL && tv_dict_add_str(d, S_LEN("file"), fname) == FAIL)) { + return FAIL; + } + + return OK; +} + + +/// Get information about marks local to a buffer. +/// +/// @param[in] buf Buffer to get the marks from +/// @param[out] l List to store marks +void get_buf_local_marks(const buf_T *buf, list_T *l) + FUNC_ATTR_NONNULL_ALL +{ + char mname[3] = "' "; + + // Marks 'a' to 'z' + for (int i = 0; i < NMARKS; i++) { + mname[1] = (char)('a' + i); + add_mark(l, mname, &buf->b_namedm[i].mark, buf->b_fnum, NULL); + } + + // Mark '' is a window local mark and not a buffer local mark + add_mark(l, "''", &curwin->w_pcmark, curbuf->b_fnum, NULL); + + add_mark(l, "'\"", &buf->b_last_cursor.mark, buf->b_fnum, NULL); + add_mark(l, "'[", &buf->b_op_start, buf->b_fnum, NULL); + add_mark(l, "']", &buf->b_op_end, buf->b_fnum, NULL); + add_mark(l, "'^", &buf->b_last_insert.mark, buf->b_fnum, NULL); + add_mark(l, "'.", &buf->b_last_change.mark, buf->b_fnum, NULL); + add_mark(l, "'<", &buf->b_visual.vi_start, buf->b_fnum, NULL); + add_mark(l, "'>", &buf->b_visual.vi_end, buf->b_fnum, NULL); +} + +/// Get information about global marks ('A' to 'Z' and '0' to '9') +/// +/// @param[out] l List to store global marks +void get_global_marks(list_T *l) + FUNC_ATTR_NONNULL_ALL +{ + char mname[3] = "' "; + char *name; + + // Marks 'A' to 'Z' and '0' to '9' + for (int i = 0; i < NMARKS + EXTRA_MARKS; i++) { + if (namedfm[i].fmark.fnum != 0) { + name = (char *)buflist_nr2name(namedfm[i].fmark.fnum, true, true); + } else { + name = (char *)namedfm[i].fname; + } + if (name != NULL) { + mname[1] = i >= NMARKS ? (char)(i - NMARKS + '0') : (char)(i + 'A'); + + add_mark(l, mname, &namedfm[i].fmark.mark, namedfm[i].fmark.fnum, name); + if (namedfm[i].fmark.fnum != 0) { + xfree(name); + } + } + } +} diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index e9ea2cbba9..b3afdefac1 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -55,7 +55,6 @@ #define T MT_BRANCH_FACTOR #define ILEN (sizeof(mtnode_t)+(2 * T) * sizeof(void *)) -#define key_t SKRAPET #define RIGHT_GRAVITY (((uint64_t)1) << 63) #define ANTIGRAVITY(id) ((id)&(RIGHT_GRAVITY-1)) @@ -65,8 +64,6 @@ #define END_FLAG MARKTREE_END_FLAG #define ID_INCR (((uint64_t)1) << 2) -#define PROP_MASK (RIGHT_GRAVITY|PAIRED|END_FLAG) - #define rawkey(itr) (itr->node->key[itr->i]) static bool pos_leq(mtpos_t a, mtpos_t b) 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/message.c b/src/nvim/message.c index f76a408481..f94529c687 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1157,15 +1157,7 @@ void wait_return(int redraw) || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE || c == K_RIGHTDRAG || c == K_RIGHTRELEASE || c == K_MOUSELEFT || c == K_MOUSERIGHT - || c == K_MOUSEDOWN || c == K_MOUSEUP - || (!mouse_has(MOUSE_RETURN) - && mouse_row < msg_row - && (c == K_LEFTMOUSE - || c == K_MIDDLEMOUSE - || c == K_RIGHTMOUSE - || c == K_X1MOUSE - || c == K_X2MOUSE)) - ); + || c == K_MOUSEDOWN || c == K_MOUSEUP); os_breakcheck(); /* * Avoid that the mouse-up event causes visual mode to start. @@ -1711,8 +1703,11 @@ void msg_prt_line(char_u *s, int list) } else if ((l = utfc_ptr2len(s)) > 1) { col += utf_ptr2cells(s); char buf[MB_MAXBYTES + 1]; - if (curwin->w_p_lcs_chars.nbsp != NUL && list - && (utf_ptr2char(s) == 160 || utf_ptr2char(s) == 0x202f)) { + if (l >= MB_MAXBYTES) { + xstrlcpy(buf, "?", sizeof(buf)); + } else if (curwin->w_p_lcs_chars.nbsp != NUL && list + && (utf_ptr2char(s) == 160 + || utf_ptr2char(s) == 0x202f)) { utf_char2bytes(curwin->w_p_lcs_chars.nbsp, (char_u *)buf); buf[utfc_ptr2len((char_u *)buf)] = NUL; } else { diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index f05dade73f..ff471ea978 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -526,53 +526,9 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp) void setmouse(void) { ui_cursor_shape(); - - // Be quick when mouse is off. - if (*p_mouse == NUL) { - return; - } - - int checkfor = MOUSE_NORMAL; // assume normal mode - if (VIsual_active) { - checkfor = MOUSE_VISUAL; - } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE) { - checkfor = MOUSE_RETURN; - } else if (State & INSERT) { - checkfor = MOUSE_INSERT; - } else if (State & CMDLINE) { - checkfor = MOUSE_COMMAND; - } else if (State == CONFIRM || State == EXTERNCMD) { - checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd" - } - - if (mouse_has(checkfor)) { - ui_call_mouse_on(); - } else { - ui_call_mouse_off(); - } + ui_check_mouse(); } -/* - * Return true if - * - "c" is in 'mouse', or - * - 'a' is in 'mouse' and "c" is in MOUSE_A, or - * - the current buffer is a help file and 'h' is in 'mouse' and we are in a - * normal editing mode (not at hit-return message). - */ -int mouse_has(int c) -{ - for (char_u *p = p_mouse; *p; ++p) - switch (*p) { - case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL) - return true; - break; - case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help) - return true; - break; - default: if (c == *p) return true; break; - } - return false; -} // Set orig_topline. Used when jumping to another window, so that a double // click still works. 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 0293bb4a73..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); @@ -2375,10 +2375,10 @@ do_mouse ( * Also paste at the cursor if the current mode isn't in 'mouse' (only * happens for the GUI). */ - if ((State & INSERT) || !mouse_has(MOUSE_NORMAL)) { - if (regname == '.') + if ((State & INSERT)) { + if (regname == '.') { insert_reg(regname, true); - else { + } else { if (regname == 0 && eval_has_provider("clipboard")) { regname = '*'; } @@ -2558,8 +2558,9 @@ do_mouse ( * on a status line */ if (VIsual_active) jump_flags |= MOUSE_MAY_STOP_VIS; - } else if (mouse_has(MOUSE_VISUAL)) + } else { jump_flags |= MOUSE_MAY_VIS; + } } else if (which_button == MOUSE_RIGHT) { if (is_click && VIsual_active) { /* @@ -2575,8 +2576,7 @@ do_mouse ( } } jump_flags |= MOUSE_FOCUS; - if (mouse_has(MOUSE_VISUAL)) - jump_flags |= MOUSE_MAY_VIS; + jump_flags |= MOUSE_MAY_VIS; } } @@ -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; @@ -2790,8 +2790,7 @@ do_mouse ( /* Handle double clicks, unless on status line */ else if (in_status_line) { } else if (in_sep_line) { - } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT)) - && mouse_has(MOUSE_VISUAL)) { + } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT))) { if (is_click || !VIsual_active) { if (VIsual_active) { orig_cursor = VIsual; @@ -3052,57 +3051,57 @@ static bool find_is_eval_item( return false; } -/* - * Find the identifier under or to the right of the cursor. - * "find_type" can have one of three values: - * FIND_IDENT: find an identifier (keyword) - * FIND_STRING: find any non-white string - * FIND_IDENT + FIND_STRING: find any non-white string, identifier preferred. - * FIND_EVAL: find text useful for C program debugging - * - * There are three steps: - * 1. Search forward for the start of an identifier/string. Doesn't move if - * already on one. - * 2. Search backward for the start of this identifier/string. - * This doesn't match the real Vi but I like it a little better and it - * shouldn't bother anyone. - * 3. Search forward to the end of this identifier/string. - * When FIND_IDENT isn't defined, we backup until a blank. - * - * Returns the length of the string, or zero if no string is found. - * If a string is found, a pointer to the string is put in "*string". This - * string is not always NUL terminated. - */ -size_t find_ident_under_cursor(char_u **string, int find_type) +// Find the identifier under or to the right of the cursor. +// "find_type" can have one of three values: +// FIND_IDENT: find an identifier (keyword) +// FIND_STRING: find any non-white text +// FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred. +// FIND_EVAL: find text useful for C program debugging +// +// There are three steps: +// 1. Search forward for the start of an identifier/text. Doesn't move if +// already on one. +// 2. Search backward for the start of this identifier/text. +// This doesn't match the real Vi but I like it a little better and it +// shouldn't bother anyone. +// 3. Search forward to the end of this identifier/text. +// When FIND_IDENT isn't defined, we backup until a blank. +// +// Returns the length of the text, or zero if no text is found. +// If text is found, a pointer to the text is put in "*text". This +// points into the current buffer line and is not always NUL terminated. +size_t find_ident_under_cursor(char_u **text, int find_type) + FUNC_ATTR_NONNULL_ARG(1) { return find_ident_at_pos(curwin, curwin->w_cursor.lnum, - curwin->w_cursor.col, string, find_type); + curwin->w_cursor.col, text, NULL, find_type); } /* * Like find_ident_under_cursor(), but for any window and any position. * However: Uses 'iskeyword' from the current window!. */ -size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, - char_u **string, int find_type) -{ - char_u *ptr; - int col = 0; /* init to shut up GCC */ +size_t find_ident_at_pos( + win_T *wp, + linenr_T lnum, + colnr_T startcol, + char_u **text, + int *textcol, // column where "text" starts, can be NULL + int find_type) + FUNC_ATTR_NONNULL_ARG(1, 4) +{ + int col = 0; // init to shut up GCC int i; int this_class = 0; int prev_class; int prevcol; int bn = 0; // bracket nesting - /* - * if i == 0: try to find an identifier - * if i == 1: try to find any non-white string - */ - ptr = ml_get_buf(wp->w_buffer, lnum, false); - for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i) { - /* - * 1. skip to start of identifier/string - */ + // if i == 0: try to find an identifier + // if i == 1: try to find any non-white text + char_u *ptr = ml_get_buf(wp->w_buffer, lnum, false); + for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; i++) { + // 1. skip to start of identifier/text col = startcol; while (ptr[col] != NUL) { // Stop at a ']' to evaluate "a[x]". @@ -3120,7 +3119,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, bn = ptr[col] == ']'; // - // 2. Back up to start of identifier/string. + // 2. Back up to start of identifier/text. // // Remember class of character under cursor. if ((find_type & FIND_EVAL) && ptr[col] == ']') { @@ -3143,7 +3142,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, col = prevcol; } - // If we don't want just any old string, or we've found an + // If we don't want just any old text, or we've found an // identifier, stop searching. if (this_class > 2) { this_class = 2; @@ -3154,7 +3153,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, } if (ptr[col] == NUL || (i == 0 && this_class != 2)) { - // Didn't find an identifier or string. + // Didn't find an identifier or text. if (find_type & FIND_STRING) { EMSG(_("E348: No string under cursor")); } else { @@ -3163,11 +3162,12 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, return 0; } ptr += col; - *string = ptr; + *text = ptr; + if (textcol != NULL) { + *textcol = col; + } - /* - * 3. Find the end if the identifier/string. - */ + // 3. Find the end if the identifier/text. bn = 0; startcol -= col; col = 0; @@ -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 40dd5f0b5c..052b07ed44 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1197,7 +1197,13 @@ int insert_reg( retval = FAIL; } else { for (size_t i = 0; i < reg->y_size; i++) { - stuffescaped((const char *)reg->y_array[i], literally); + if (regname == '-') { + AppendCharToRedobuff(Ctrl_R); + AppendCharToRedobuff(regname); + do_put(regname, NULL, BACKWARD, 1L, PUT_CURSEND); + } else { + stuffescaped((const char *)reg->y_array[i], literally); + } // Insert a newline between lines and after last line if // y_type is kMTLineWise. if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { @@ -1683,6 +1689,7 @@ int op_delete(oparg_T *oap) (int)oap->line_count-1, n, deleted_bytes, 0, 0, 0, kExtmarkUndo); } + auto_format(false, true); } msgmore(curbuf->b_ml.ml_line_count - old_lcount); @@ -3547,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 */ @@ -3580,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); @@ -3609,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); } /* @@ -3618,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); } /* @@ -3627,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); } /* @@ -3639,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); } } @@ -3649,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); } /* @@ -3658,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); } } @@ -3669,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 0034117ddc..47b9e9bb07 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -206,7 +206,6 @@ typedef struct vimoption { // local option: indirect option index char_u *def_val[2]; // default values for variable (vi and vim) LastSet last_set; // script in which the option was last set -# define SCTX_INIT , { 0, 0, 0 } } vimoption_T; #define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value @@ -307,7 +306,7 @@ static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix", static char *(p_bufhidden_values[]) = { "hide", "unload", "delete", "wipe", NULL }; -static char *(p_bs_values[]) = { "indent", "eol", "start", NULL }; +static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL }; static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", "syntax", "diff", NULL }; static char *(p_fcl_values[]) = { "all", NULL }; @@ -321,7 +320,7 @@ static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", "yes:1", "yes:2", "yes:3", "yes:4", "yes:5", "yes:6", "yes:7", "yes:8", "yes:9", "number", NULL }; -static char *(p_fdc_values[]) = { "auto:1", "auto:2", +static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; @@ -352,9 +351,6 @@ void set_init_1(bool clean_arg) langmap_init(); - // Be nocompatible - p_cp = false; - /* * Find default value for 'shell' option. * Don't use it if it is empty. @@ -683,7 +679,7 @@ set_options_default( { for (int i = 0; options[i].fullname; i++) { if (!(options[i].flags & P_NODEFAULT)) { - set_option_default(i, opt_flags, p_cp); + set_option_default(i, opt_flags, false); } } @@ -763,7 +759,7 @@ void set_init_2(bool headless) // which results in the actual value computed from the window height. idx = findoption("scroll"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { - set_option_default(idx, OPT_LOCAL, p_cp); + set_option_default(idx, OPT_LOCAL, false); } comp_col(); @@ -1113,7 +1109,7 @@ int do_set( if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL) { arg += len; - cp_val = p_cp; + cp_val = false; if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') { if (arg[3] == 'm') { // "opt&vim": set to Vim default cp_val = false; @@ -1370,6 +1366,10 @@ int do_set( *(char_u **)varp = vim_strsave( (char_u *)"indent,eol,start"); break; + case 3: + *(char_u **)varp = vim_strsave( + (char_u *)"indent,eol,nostop"); + break; } xfree(oldval); if (origval == oldval) { @@ -2943,7 +2943,7 @@ ambw_end: } } else if (varp == &p_bs) { // 'backspace' if (ascii_isdigit(*p_bs)) { - if (*p_bs >'2' || p_bs[1] != NUL) { + if (*p_bs > '3' || p_bs[1] != NUL) { errmsg = e_invarg; } } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) { @@ -3214,11 +3214,7 @@ ambw_end: } if (varp == &p_mouse) { - if (*p_mouse == NUL) { - ui_call_mouse_off(); - } else { - setmouse(); // in case 'mouse' changed - } + setmouse(); // in case 'mouse' changed } if (curwin->w_curswant != MAXCOL @@ -3617,10 +3613,14 @@ static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx) { int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int indir = (int)options[opt_idx].indir; - const LastSet last_set = { .script_ctx = - { script_ctx.sc_sid, script_ctx.sc_seq, - script_ctx.sc_lnum + sourcing_lnum }, - current_channel_id }; + const LastSet last_set = { + .script_ctx = { + script_ctx.sc_sid, + script_ctx.sc_seq, + script_ctx.sc_lnum + sourcing_lnum + }, + current_channel_id + }; // Remember where the option was set. For local options need to do that // in the buffer or window structure. @@ -4338,6 +4338,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", @@ -4950,7 +4951,7 @@ static int optval_default(vimoption_T *p, char_u *varp) if (varp == NULL) { return true; // hidden option is always at default } - dvi = ((p->flags & P_VI_DEF) || p_cp) ? VI_DEFAULT : VIM_DEFAULT; + dvi = (p->flags & P_VI_DEF) ? VI_DEFAULT : VIM_DEFAULT; if (p->flags & P_NUM) { return *(long *)varp == (long)(intptr_t)p->def_val[dvi]; } @@ -4983,11 +4984,7 @@ void ui_refresh_options(void) ui_call_option_set(name, value); } if (p_mouse != NULL) { - if (*p_mouse == NUL) { - ui_call_mouse_off(); - } else { - setmouse(); - } + setmouse(); } } @@ -6808,15 +6805,15 @@ static int check_opt_wim(void) } /// Check if backspacing over something is allowed. -/// The parameter what is one of the following: whatBS_INDENT, BS_EOL -/// or BS_START +/// @param what BS_INDENT, BS_EOL, BS_START, or BS_NOSTOP bool can_bs(int what) { if (what == BS_START && bt_prompt(curbuf)) { return false; } switch (*p_bs) { - case '2': return true; + case '3': return true; + case '2': return what != BS_NOSTOP; case '1': return what != BS_START; case '0': return false; } @@ -7159,18 +7156,90 @@ 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) +{ + int opt_idx = findoption_len((const char *)name.data, name.size); + if (opt_idx < 0) { + api_set_error(err, kErrorTypeValidation, "no such option: '%s'", name.data); + return (Dictionary)ARRAY_DICT_INIT; + } + return vimoption2dict(&options[opt_idx]); +} + +Dictionary get_all_vimoptions(void) +{ + Dictionary retval = ARRAY_DICT_INIT; + for (size_t i = 0; i < PARAM_COUNT; i++) { + Dictionary opt_dict = vimoption2dict(&options[i]); + PUT(retval, options[i].fullname, DICTIONARY_OBJ(opt_dict)); + } + return retval; +} + +static Dictionary vimoption2dict(vimoption_T *opt) +{ + Dictionary dict = ARRAY_DICT_INIT; + + PUT(dict, "name", CSTR_TO_OBJ(opt->fullname)); + PUT(dict, "shortname", CSTR_TO_OBJ(opt->shortname)); + + const char *scope; + if (opt->indir & PV_BUF) { + scope = "buf"; + } else if (opt->indir & PV_WIN) { + scope = "win"; + } else { + scope = "global"; + } + + PUT(dict, "scope", CSTR_TO_OBJ(scope)); + + // welcome to the jungle + PUT(dict, "global_local", BOOL(opt->indir & PV_BOTH)); + PUT(dict, "commalist", BOOL(opt->flags & P_COMMA)); + PUT(dict, "flaglist", BOOL(opt->flags & P_FLAGLIST)); + + PUT(dict, "was_set", BOOL(opt->flags & P_WAS_SET)); + + PUT(dict, "last_set_sid", INTEGER_OBJ(opt->last_set.script_ctx.sc_sid)); + PUT(dict, "last_set_linenr", INTEGER_OBJ(opt->last_set.script_ctx.sc_lnum)); + PUT(dict, "last_set_chan", INTEGER_OBJ((int64_t)opt->last_set.channel_id)); + + const char *type; + Object def; + // TODO(bfredl): do you even nocp? + char_u *def_val = opt->def_val[(opt->flags & P_VI_DEF) + ? VI_DEFAULT : VIM_DEFAULT]; + if (opt->flags & P_STRING) { + type = "string"; + def = CSTR_TO_OBJ(def_val ? (char *)def_val : ""); + } else if (opt->flags & P_NUM) { + type = "number"; + def = INTEGER_OBJ((Integer)(intptr_t)def_val); + } else if (opt->flags & P_BOOL) { + type = "boolean"; + def = BOOL((intptr_t)def_val); + } else { + type = ""; def = NIL; + } + PUT(dict, "type", CSTR_TO_OBJ(type)); + PUT(dict, "default", def); + + return dict; } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index af0ea7f4a2..683afc670e 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -282,9 +282,14 @@ enum { #define WIM_BUFLASTUSED 8 // arguments for can_bs() +// each defined char should be unique over all values +// except for BS_START, that intentionally also matches BS_NOSTOP +// because BS_NOSTOP behaves exactly the same except it +// does not stop at the start of the insert point #define BS_INDENT 'i' // "Indent" -#define BS_EOL 'o' // "eOl" +#define BS_EOL 'l' // "eoL" #define BS_START 's' // "Start" +#define BS_NOSTOP 'p' // "nostoP #define LISPWORD_VALUE \ "defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object" @@ -372,7 +377,6 @@ EXTERN long p_cwh; // 'cmdwinheight' EXTERN long p_ch; // 'cmdheight' EXTERN long p_columns; // 'columns' EXTERN int p_confirm; // 'confirm' -EXTERN int p_cp; // 'compatible' EXTERN char_u *p_cot; // 'completeopt' # ifdef BACKSLASH_IN_FILENAME EXTERN char_u *p_csl; // 'completeslash' 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/os/env.c b/src/nvim/os/env.c index 879266e3d4..008f5ef63b 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -394,13 +394,21 @@ void os_get_hostname(char *hostname, size_t size) } /// To get the "real" home directory: -/// - get value of $HOME +/// 1. get value of $HOME +/// 2. if $HOME is not set, try the following +/// For Windows: +/// 1. assemble homedir using HOMEDRIVE and HOMEPATH +/// 2. try os_homedir() +/// 3. resolve a direct reference to another system variable +/// 4. guess C drive /// For Unix: -/// - go to that directory -/// - do os_dirname() to get the real name of that directory. -/// This also works with mounts and links. -/// Don't do this for Windows, it will change the "current dir" for a drive. +/// 1. try os_homedir() +/// 2. go to that directory +/// This also works with mounts and links. +/// Don't do this for Windows, it will change the "current dir" for a drive. +/// 3. fall back to current working directory as a last resort static char *homedir = NULL; +static char *os_homedir(void); void init_homedir(void) { @@ -430,7 +438,7 @@ void init_homedir(void) } } if (var == NULL) { - var = os_getenv("USERPROFILE"); + var = os_homedir(); } // Weird but true: $HOME may contain an indirect reference to another @@ -440,6 +448,7 @@ void init_homedir(void) const char *p = strchr(var + 1, '%'); if (p != NULL) { vim_snprintf(os_buf, (size_t)(p - var), "%s", var + 1); + var = NULL; const char *exp = os_getenv(os_buf); if (exp != NULL && *exp != NUL && STRLEN(exp) + STRLEN(p) < MAXPATHL) { @@ -458,8 +467,12 @@ void init_homedir(void) } #endif - if (var != NULL) { #ifdef UNIX + if (var == NULL) { + var = os_homedir(); + } + + if (var != NULL) { // Change to the directory and get the actual path. This resolves // links. Don't do it when we can't return. if (os_dirname((char_u *)os_buf, MAXPATHL) == OK && os_chdir(os_buf) == 0) { @@ -470,11 +483,37 @@ void init_homedir(void) EMSG(_(e_prev_dir)); } } + } + + // Fall back to current working directory if home is not found + if ((var == NULL || *var == NUL) + && os_dirname((char_u *)os_buf, sizeof(os_buf)) == OK) { + var = os_buf; + } #endif + if (var != NULL) { homedir = xstrdup(var); } } +static char homedir_buf[MAXPATHL]; + +static char *os_homedir(void) +{ + homedir_buf[0] = NUL; + size_t homedir_size = MAXPATHL; + uv_mutex_lock(&mutex); + // http://docs.libuv.org/en/v1.x/misc.html#c.uv_os_homedir + int ret_value = uv_os_homedir(homedir_buf, &homedir_size); + uv_mutex_unlock(&mutex); + if (ret_value == 0 && homedir_size < MAXPATHL) { + return homedir_buf; + } + ELOG("uv_os_homedir() failed %d: %s", ret_value, os_strerror(ret_value)); + homedir_buf[0] = NUL; + return NULL; +} + #if defined(EXITFREE) void free_homedir(void) diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index b7878d9da8..9d6518841a 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -393,7 +393,7 @@ static InbufPollResult inbuf_poll(int ms, MultiQueue *events) prof_inchar_enter(); } - if ((ms == - 1 || ms > 0) && events == NULL && !input_eof) { + if ((ms == - 1 || ms > 0) && events != main_loop.events && !input_eof) { // The pending input provoked a blocking wait. Do special events now. #6247 blocking = true; multiqueue_process_events(ch_before_blocking_events); diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 603191a0ff..b63faacaae 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -8,9 +8,12 @@ # undef Boolean #endif +#include "auto/config.h" + #ifdef HAVE_LOCALE_H # include <locale.h> #endif + #include "nvim/os/lang.h" #include "nvim/os/os.h" diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 9374693550..16e17a9c60 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -5,6 +5,8 @@ #include <uv.h> +#include "auto/config.h" + #include "nvim/ascii.h" #include "nvim/os/os.h" #include "nvim/garray.h" diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index f242b7d71a..aef7ffa397 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -742,6 +742,7 @@ static int pum_set_selected(int n, int repeat) // Edit a new, empty buffer. Set options for a "wipeout" // buffer. set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bl", 0L, NULL, OPT_LOCAL); set_option_value("bt", 0L, "nofile", OPT_LOCAL); set_option_value("bh", 0L, "wipe", OPT_LOCAL); set_option_value("diff", 0L, NULL, OPT_LOCAL); @@ -808,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 7fdc998e20..a625c09f78 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -143,6 +143,7 @@ struct efm_S { // 'E' error message // 'W' warning message // 'I' informational message + // 'N' note message // 'C' continuation line // 'Z' end of multi-line message // 'G' general, unspecific message @@ -457,7 +458,7 @@ static const char_u *efm_analyze_prefix(const char_u *efmp, efm_T *efminfo, if (vim_strchr((char_u *)"+-", *efmp) != NULL) { efminfo->flags = *efmp++; } - if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) { + if (vim_strchr((char_u *)"DXAEWINCZGOPQ", *efmp) != NULL) { efminfo->prefix = *efmp; } else { snprintf((char *)errmsg, errmsglen, @@ -967,7 +968,7 @@ restofline: fmt_start = fmt_ptr; } - if (vim_strchr((char_u *)"AEWI", idx) != NULL) { + if (vim_strchr((char_u *)"AEWIN", idx) != NULL) { qfl->qf_multiline = true; // start of a multi-line message qfl->qf_multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != NULL) { @@ -1499,7 +1500,7 @@ static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, if ((idx == 'C' || idx == 'Z') && !qf_multiline) { return QF_FAIL; } - if (vim_strchr((char_u *)"EWI", idx) != NULL) { + if (vim_strchr((char_u *)"EWIN", idx) != NULL) { fields->type = idx; } else { fields->type = 0; @@ -3300,6 +3301,24 @@ void qf_history(exarg_T *eap) qf_info_T *qi = qf_cmd_get_stack(eap, false); int i; + if (eap->addr_count > 0) { + if (qi == NULL) { + EMSG(_(e_loclist)); + return; + } + + // Jump to the specified quickfix list + if (eap->line2 > 0 && eap->line2 <= qi->qf_listcount) { + qi->qf_curlist = (int)(eap->line2 - 1); + qf_msg(qi, qi->qf_curlist, ""); + qf_update_buffer(qi, NULL); + } else { + EMSG(_(e_invrange)); + } + + return; + } + if (qf_stack_empty(qi)) { MSG(_("No entries")); } else { @@ -3414,11 +3433,13 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, // e or E 0 " error" // w or W 0 " warning" // i or I 0 " info" +// n or N 0 " note" // 0 0 "" // other 0 " c" // e or E n " error n" // w or W n " warning n" // i or I n " info n" +// n or N n " note n" // 0 n " error n" // other n " c n" // 1 x "" :helpgrep @@ -3428,15 +3449,17 @@ static char_u *qf_types(int c, int nr) static char_u cc[3]; char_u *p; - if (c == 'W' || c == 'w') + if (c == 'W' || c == 'w') { p = (char_u *)" warning"; - else if (c == 'I' || c == 'i') + } else if (c == 'I' || c == 'i') { p = (char_u *)" info"; - else if (c == 'E' || c == 'e' || (c == 0 && nr > 0)) + } else if (c == 'N' || c == 'n') { + p = (char_u *)" note"; + } else if (c == 'E' || c == 'e' || (c == 0 && nr > 0)) { p = (char_u *)" error"; - else if (c == 0 || c == 1) + } else if (c == 0 || c == 1) { p = (char_u *)""; - else { + } else { cc[0] = ' '; cc[1] = (char_u)c; cc[2] = NUL; @@ -3622,7 +3645,7 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height) // Set the options for the quickfix buffer/window (if not already done) // Do this even if the quickfix buffer was already present, as an autocmd // might have previously deleted (:bdelete) the quickfix buffer. - if (curbuf->b_p_bt[0] != 'q') { + if (!bt_quickfix(curbuf)) { qf_set_cwindow_options(); } @@ -3696,7 +3719,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 +3733,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; @@ -3824,17 +3847,21 @@ static buf_T *qf_find_buf(qf_info_T *qi) return NULL; } -/// Update the w:quickfix_title variable in the quickfix/location list window +/// Update the w:quickfix_title variable in the quickfix/location list window in +/// all the tab pages. static void qf_update_win_titlevar(qf_info_T *qi) + FUNC_ATTR_NONNULL_ALL { - win_T *win; + qf_list_T *const qfl = qf_get_curlist(qi); + win_T *const save_curwin = curwin; - if ((win = qf_find_win(qi)) != NULL) { - win_T *curwin_save = curwin; - curwin = win; - qf_set_title_var(qf_get_curlist(qi)); - curwin = curwin_save; + FOR_ALL_TAB_WINDOWS(tp, win) { + if (is_qf_win(win, qi)) { + curwin = win; + qf_set_title_var(qfl); + } } + curwin = save_curwin; } // Find the quickfix buffer. If it exists, update the contents. @@ -4265,11 +4292,22 @@ static char_u *get_mef_name(void) return name; } -/// Returns the number of valid entries in the current quickfix/location list. +/// Returns the number of entries in the current quickfix/location list. size_t qf_get_size(exarg_T *eap) FUNC_ATTR_NONNULL_ALL { qf_info_T *qi; + + if ((qi = qf_cmd_get_stack(eap, false)) == NULL) { + return 0; + } + return (size_t)qf_get_curlist(qi)->qf_count; +} + +/// Returns the number of valid entries in the current quickfix/location list. +size_t qf_get_valid_size(exarg_T *eap) +{ + qf_info_T *qi; qf_list_T *qfl; if ((qi = qf_cmd_get_stack(eap, false)) == NULL) { @@ -4558,74 +4596,150 @@ static qfline_T * qf_find_last_entry_on_line(qfline_T *entry, int *errornr) return entry; } -/// Find the first quickfix entry below line 'lnum' in buffer 'bnr'. +// Returns true if the specified quickfix entry is +// after the given line (linewise is true) +// or after the line and column. +static bool qf_entry_after_pos(const qfline_T *qfp, const pos_T *pos, + bool linewise) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (linewise) { + return qfp->qf_lnum > pos->lnum; + } + return qfp->qf_lnum > pos->lnum + || (qfp->qf_lnum == pos->lnum && qfp->qf_col > pos->col); +} + +// Returns true if the specified quickfix entry is +// before the given line (linewise is true) +// or before the line and column. +static bool qf_entry_before_pos(const qfline_T *qfp, const pos_T *pos, + bool linewise) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (linewise) { + return qfp->qf_lnum < pos->lnum; + } + return qfp->qf_lnum < pos->lnum + || (qfp->qf_lnum == pos->lnum && qfp->qf_col < pos->col); +} + +// Returns true if the specified quickfix entry is +// on or after the given line (linewise is true) +// or on or after the line and column. +static bool qf_entry_on_or_after_pos(const qfline_T *qfp, const pos_T *pos, + bool linewise) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (linewise) { + return qfp->qf_lnum >= pos->lnum; + } + return qfp->qf_lnum > pos->lnum + || (qfp->qf_lnum == pos->lnum && qfp->qf_col >= pos->col); +} + +// Returns true if the specified quickfix entry is +// on or before the given line (linewise is true) +// or on or before the line and column. +static bool qf_entry_on_or_before_pos(const qfline_T *qfp, const pos_T *pos, + bool linewise) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (linewise) { + return qfp->qf_lnum <= pos->lnum; + } + return qfp->qf_lnum < pos->lnum + || (qfp->qf_lnum == pos->lnum && qfp->qf_col <= pos->col); +} + +/// Find the first quickfix entry after position 'pos' in buffer 'bnr'. +/// If 'linewise' is true, returns the entry after the specified line and treats +/// multiple entries on a single line as one. Otherwise returns the entry after +/// the specified line and column. /// 'qfp' points to the very first entry in the buffer and 'errornr' is the /// index of the very first entry in the quickfix list. -/// Returns NULL if an entry is not found after 'lnum'. -static qfline_T *qf_find_entry_on_next_line(int bnr, - linenr_T lnum, - qfline_T *qfp, - int *errornr) +/// Returns NULL if an entry is not found after 'pos'. +static qfline_T *qf_find_entry_after_pos( + int bnr, + const pos_T *pos, + bool linewise, + qfline_T *qfp, + int *errornr +) + FUNC_ATTR_NONNULL_ALL { - if (qfp->qf_lnum > lnum) { - // First entry is after line 'lnum' + if (qf_entry_after_pos(qfp, pos, linewise)) { + // First entry is after postion 'pos' return qfp; } - // Find the entry just before or at the line 'lnum' + // Find the entry just before or at the position 'pos' while (qfp->qf_next != NULL && qfp->qf_next->qf_fnum == bnr - && qfp->qf_next->qf_lnum <= lnum) { + && qf_entry_on_or_before_pos(qfp->qf_next, pos, linewise)) { qfp = qfp->qf_next; (*errornr)++; } if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr) { - // No entries found after 'lnum' + // No entries found after position 'pos' return NULL; } - // Use the entry just after line 'lnum' + // Use the entry just after position 'pos' qfp = qfp->qf_next; (*errornr)++; return qfp; } -/// Find the first quickfix entry before line 'lnum' in buffer 'bnr'. +/// Find the first quickfix entry before position 'pos' in buffer 'bnr'. +/// If 'linewise' is true, returns the entry before the specified line and +/// treats multiple entries on a single line as one. Otherwise returns the entry +/// before the specified line and column. /// 'qfp' points to the very first entry in the buffer and 'errornr' is the /// index of the very first entry in the quickfix list. -/// Returns NULL if an entry is not found before 'lnum'. -static qfline_T *qf_find_entry_on_prev_line(int bnr, - linenr_T lnum, - qfline_T *qfp, - int *errornr) +/// Returns NULL if an entry is not found before 'pos'. +static qfline_T *qf_find_entry_before_pos( + int bnr, + const pos_T *pos, + bool linewise, + qfline_T *qfp, + int *errornr +) + FUNC_ATTR_NONNULL_ALL { - // Find the entry just before the line 'lnum' + // Find the entry just before the position 'pos' while (qfp->qf_next != NULL && qfp->qf_next->qf_fnum == bnr - && qfp->qf_next->qf_lnum < lnum) { + && qf_entry_before_pos(qfp->qf_next, pos, linewise)) { qfp = qfp->qf_next; (*errornr)++; } - if (qfp->qf_lnum >= lnum) { // entry is after 'lnum' + if (qf_entry_on_or_after_pos(qfp, pos, linewise)) { return NULL; } - // If multiple entries are on the same line, then use the first entry - qfp = qf_find_first_entry_on_line(qfp, errornr); + if (linewise) { + // If multiple entries are on the same line, then use the first entry + qfp = qf_find_first_entry_on_line(qfp, errornr); + } return qfp; } -/// Find a quickfix entry in 'qfl' closest to line 'lnum' in buffer 'bnr' in +/// Find a quickfix entry in 'qfl' closest to position 'pos' in buffer 'bnr' in /// the direction 'dir'. -static qfline_T *qf_find_closest_entry(qf_list_T *qfl, - int bnr, - linenr_T lnum, - int dir, - int *errornr) +static qfline_T *qf_find_closest_entry( + qf_list_T *qfl, + int bnr, + const pos_T *pos, + Direction dir, + bool linewise, + int *errornr +) + FUNC_ATTR_NONNULL_ALL { qfline_T *qfp; @@ -4638,33 +4752,38 @@ static qfline_T *qf_find_closest_entry(qf_list_T *qfl, } if (dir == FORWARD) { - qfp = qf_find_entry_on_next_line(bnr, lnum, qfp, errornr); + qfp = qf_find_entry_after_pos(bnr, pos, linewise, qfp, errornr); } else { - qfp = qf_find_entry_on_prev_line(bnr, lnum, qfp, errornr); + qfp = qf_find_entry_before_pos(bnr, pos, linewise, qfp, errornr); } return qfp; } -/// Get the nth quickfix entry below the specified entry treating multiple -/// entries on a single line as one. Searches forward in the list. -static void qf_get_nth_below_entry(qfline_T *entry, - int *errornr, - linenr_T n) +/// Get the nth quickfix entry below the specified entry. Searches forward in +/// the list. If linewise is true, then treat multiple entries on a single line +/// as one. +static void qf_get_nth_below_entry(qfline_T *entry, linenr_T n, + bool linewise, int *errornr) + FUNC_ATTR_NONNULL_ALL { while (n-- > 0 && !got_int) { // qfline_T *first_entry = entry; int first_errornr = *errornr; - // Treat all the entries on the same line in this file as one - entry = qf_find_last_entry_on_line(entry, errornr); + if (linewise) { + // Treat all the entries on the same line in this file as one + entry = qf_find_last_entry_on_line(entry, errornr); + } if (entry->qf_next == NULL || entry->qf_next->qf_fnum != entry->qf_fnum) { - // If multiple entries are on the same line, then use the first - // entry - // entry = first_entry; - *errornr = first_errornr; + if (linewise) { + // If multiple entries are on the same line, then use the first + // entry + // entry = first_entry; + *errornr = first_errornr; + } break; } @@ -4673,11 +4792,12 @@ static void qf_get_nth_below_entry(qfline_T *entry, } } -/// Get the nth quickfix entry above the specified entry treating multiple -/// entries on a single line as one. Searches backwards in the list. -static void qf_get_nth_above_entry(qfline_T *entry, - int *errornr, - linenr_T n) +/// Get the nth quickfix entry above the specified entry. Searches backwards in +/// the list. If linewise is TRUE, then treat multiple entries on a single line +/// as one. +static void qf_get_nth_above_entry(qfline_T *entry, linenr_T n, + bool linewise, int *errornr) + FUNC_ATTR_NONNULL_ALL { while (n-- > 0 && !got_int) { if (entry->qf_prev == NULL @@ -4688,25 +4808,30 @@ static void qf_get_nth_above_entry(qfline_T *entry, entry = entry->qf_prev; (*errornr)--; - // If multiple entries are on the same line, then use the first entry - entry = qf_find_first_entry_on_line(entry, errornr); + if (linewise) { + entry = qf_find_first_entry_on_line(entry, errornr); + } } } -/// Find the n'th quickfix entry adjacent to line 'lnum' in buffer 'bnr' in the -/// specified direction. -/// Returns the error number in the quickfix list or 0 if an entry is not found. -static int qf_find_nth_adj_entry(qf_list_T *qfl, - int bnr, - linenr_T lnum, - linenr_T n, - int dir) +/// Find the n'th quickfix entry adjacent to position 'pos' in buffer 'bnr' in +/// the specified direction. Returns the error number in the quickfix list or 0 +/// if an entry is not found. +static int qf_find_nth_adj_entry( + qf_list_T *qfl, + int bnr, + pos_T *pos, + linenr_T n, + Direction dir, + bool linewise +) + FUNC_ATTR_NONNULL_ALL { - qfline_T *adj_entry; int errornr; - // Find an entry closest to the specified line - adj_entry = qf_find_closest_entry(qfl, bnr, lnum, dir, &errornr); + // Find an entry closest to the specified position + qfline_T *const adj_entry = qf_find_closest_entry(qfl, bnr, pos, dir, + linewise, &errornr); if (adj_entry == NULL) { return 0; } @@ -4714,24 +4839,25 @@ static int qf_find_nth_adj_entry(qf_list_T *qfl, if (--n > 0) { // Go to the n'th entry in the current buffer if (dir == FORWARD) { - qf_get_nth_below_entry(adj_entry, &errornr, n); + qf_get_nth_below_entry(adj_entry, n, linewise, &errornr); } else { - qf_get_nth_above_entry(adj_entry, &errornr, n); + qf_get_nth_above_entry(adj_entry, n, linewise, &errornr); } } return errornr; } -/// Jump to a quickfix entry in the current file nearest to the current line. -/// ":cabove", ":cbelow", ":labove" and ":lbelow" commands +/// Jump to a quickfix entry in the current file nearest to the current line or +/// current line/col. +/// ":cabove", ":cbelow", ":labove", ":lbelow", ":cafter", ":cbefore", +/// ":lafter" and ":lbefore" commands void ex_cbelow(exarg_T *eap) { qf_info_T *qi; qf_list_T *qfl; int dir; int buf_has_flag; - int errornr = 0; if (eap->addr_count > 0 && eap->line2 <= 0) { EMSG(_(e_invrange)); @@ -4739,7 +4865,8 @@ void ex_cbelow(exarg_T *eap) } // Check whether the current buffer has any quickfix entries - if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow) { + if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_cbefore || eap->cmdidx == CMD_cafter) { buf_has_flag = BUF_HAS_QF_ENTRY; } else { buf_has_flag = BUF_HAS_LL_ENTRY; @@ -4760,14 +4887,30 @@ void ex_cbelow(exarg_T *eap) return; } - if (eap->cmdidx == CMD_cbelow || eap->cmdidx == CMD_lbelow) { + if (eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_lbelow + || eap->cmdidx == CMD_cafter + || eap->cmdidx == CMD_lafter) { + // Forward motion commands dir = FORWARD; } else { dir = BACKWARD; } - errornr = qf_find_nth_adj_entry(qfl, curbuf->b_fnum, curwin->w_cursor.lnum, - eap->addr_count > 0 ? eap->line2 : 0, dir); + pos_T pos = curwin->w_cursor; + // A quickfix entry column number is 1 based whereas cursor column + // number is 0 based. Adjust the column number. + pos.col++; + const int errornr = qf_find_nth_adj_entry( + qfl, + curbuf->b_fnum, + &pos, + eap->addr_count > 0 ? eap->line2 : 0, + dir, + eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_lbelow + || eap->cmdidx == CMD_cabove + || eap->cmdidx == CMD_labove); if (errornr > 0) { qf_jump(qi, 0, errornr, false); @@ -6814,8 +6957,9 @@ void ex_helpgrep(exarg_T *eap) if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true, curbuf); - if (!new_qi && IS_LL_STACK(qi) && qf_find_buf(qi) == NULL) { - // autocommands made "qi" invalid + // When adding a location list to an existing location list stack, + // if the autocmd made the stack invalid, then just return. + if (!new_qi && IS_LL_STACK(qi) && qf_find_win_with_loclist(qi) == NULL) { decr_quickfix_busy(); return; } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 1c88bd4ba4..a2589ac431 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3605,6 +3605,24 @@ theend: if (backpos.ga_maxlen > BACKPOS_INITIAL) ga_clear(&backpos); + if (retval > 0) { + // Make sure the end is never before the start. Can happen when \zs + // and \ze are used. + if (REG_MULTI) { + const lpos_T *const start = &rex.reg_mmatch->startpos[0]; + const lpos_T *const end = &rex.reg_mmatch->endpos[0]; + + if (end->lnum < start->lnum + || (end->lnum == start->lnum && end->col < start->col)) { + rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0]; + } + } else { + if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) { + rex.reg_match->endp[0] = rex.reg_match->startp[0]; + } + } + } + return retval; } @@ -3706,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]); } } } @@ -6547,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; @@ -7066,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 7dfd16fb4f..8b5ee59d40 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -5243,9 +5243,12 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, switch (t->state->c) { case NFA_MATCH: { - // If the match ends before a composing characters and - // rex.reg_icombine is not set, that is not really a match. - if (!rex.reg_icombine && utf_iscomposing(curc)) { + // If the match is not at the start of the line, ends before a + // composing characters and rex.reg_icombine is not set, that + // is not really a match. + if (!rex.reg_icombine + && rex.input != rex.line + && utf_iscomposing(curc)) { break; } nfa_match = true; @@ -6477,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); } } } @@ -6591,6 +6593,24 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, #endif theend: + if (retval > 0) { + // Make sure the end is never before the start. Can happen when \zs and + // \ze are used. + if (REG_MULTI) { + const lpos_T *const start = &rex.reg_mmatch->startpos[0]; + const lpos_T *const end = &rex.reg_mmatch->endpos[0]; + + if (end->lnum < start->lnum + || (end->lnum == start->lnum && end->col < start->col)) { + rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0]; + } + } else { + if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) { + rex.reg_match->endp[0] = rex.reg_match->startp[0]; + } + } + } + return retval; } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2eeeebb88d..a78f905a70 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) { @@ -2415,8 +2417,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - if (wp->w_p_list) { - if (curwin->w_p_lcs_chars.space + if (wp->w_p_list && !has_fold) { + if (wp->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail || wp->w_p_lcs_chars.nbsp) { extra_check = true; @@ -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; @@ -2663,7 +2665,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - //sign column + // sign column, this is hit until sign_idx reaches count if (draw_state == WL_SIGN - 1 && n_extra == 0) { draw_state = WL_SIGN; /* Show the sign column when there are any signs in this @@ -2881,8 +2883,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && vcol == 0 && n_extra == 0 && row == startrow) { @@ -2903,8 +2904,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && col < grid->Columns && n_extra == 0 && row == startrow) { @@ -2916,8 +2916,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && col >= grid->Columns && n_extra != 0 && row == startrow) { @@ -3085,7 +3084,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, || vcol < fromcol || vcol_prev < fromcol_prev || vcol >= tocol)) { char_attr = line_attr; - } else { + } else { attr_pri = false; if (has_syntax) { char_attr = syntax_attr; @@ -3740,7 +3739,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } wp->w_wrow = row; did_wcol = true; - curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; + wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } // Don't override visual selection highlighting. @@ -3833,9 +3832,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // Add a blank character to highlight. schar_from_ascii(linebuf_char[off], ' '); } - if (area_attr == 0) { - /* Use attributes from match with highest priority among - * 'search_hl' and the match list. */ + if (area_attr == 0 && !has_fold) { + // Use attributes from match with highest priority among + // 'search_hl' and the match list. char_attr = search_hl.attr; cur = wp->w_match_head; shl_flag = FALSE; @@ -3883,8 +3882,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 @@ -4051,6 +4050,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && !wp->w_p_wrap && filler_todo <= 0 && (wp->w_p_rl ? col == 0 : col == grid->Columns - 1) + && !has_fold && (*ptr != NUL || lcs_eol_one > 0 || (n_extra && (c_extra != NUL || *p_extra != NUL)))) { @@ -4345,6 +4345,10 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) // Get information needed to display the sign in line 'lnum' in window 'wp'. // If 'nrcol' is TRUE, the sign is going to be displayed in the number column. // Otherwise the sign is going to be displayed in the sign column. +// +// @param count max number of signs +// @param[out] n_extrap number of characters from pp_extra to display +// @param[in, out] sign_idxp Index of the displayed sign static void get_sign_display_info( bool nrcol, win_T *wp, @@ -4421,6 +4425,8 @@ static void get_sign_display_info( (*sign_idxp)++; if (*sign_idxp < count) { *draw_statep = WL_SIGN - 1; + } else { + *sign_idxp = 0; } } @@ -7380,7 +7386,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/search.c b/src/nvim/search.c index 90e1e25de2..787a464070 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -89,7 +89,7 @@ static struct spat spats[2] = static int last_idx = 0; /* index in spats[] for RE_LAST */ static char_u lastc[2] = { NUL, NUL }; // last character searched for -static int lastcdir = FORWARD; // last direction of character search +static Direction lastcdir = FORWARD; // last direction of character search static int last_t_cmd = true; // last search t_cmd static char_u lastc_bytes[MB_MAXBYTES + 1]; static int lastc_bytelen = 1; // >1 for multi-byte char @@ -437,7 +437,7 @@ void set_last_csearch(int c, char_u *s, int len) memset(lastc_bytes, 0, sizeof(lastc_bytes)); } -void set_csearch_direction(int cdir) +void set_csearch_direction(Direction cdir) { lastcdir = cdir; } @@ -1430,7 +1430,7 @@ end_do_search: * ADDING is set. If p_ic is set then the pattern must be in lowercase. * Return OK for success, or FAIL if no line found. */ -int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) +int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat) { linenr_T start = 0; char_u *ptr; @@ -1496,10 +1496,11 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) * Return FAIL or OK. */ int searchc(cmdarg_T *cap, int t_cmd) + FUNC_ATTR_NONNULL_ALL { - int c = cap->nchar; /* char to search for */ - int dir = cap->arg; /* TRUE for searching forward */ - long count = cap->count1; /* repeat count */ + int c = cap->nchar; // char to search for + Direction dir = cap->arg; // TRUE for searching forward + long count = cap->count1; // repeat count int col; char_u *p; int len; @@ -4462,7 +4463,7 @@ static void search_stat(int dirc, pos_T *pos, void find_pattern_in_path( char_u *ptr, // pointer to search pattern - int dir, // direction of expansion + Direction dir, // direction of expansion size_t len, // length of search pattern bool whole, // match whole words only bool skip_comments, // don't match inside comments 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/spell.c b/src/nvim/spell.c index 797fe41320..5714f5e425 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -79,7 +79,6 @@ /* for offsetof() */ #include <stddef.h> -#include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/spell.h" #include "nvim/buffer.h" @@ -6653,7 +6652,7 @@ void spell_dump_compl ( char_u *pat, // leading part of the word int ic, // ignore case - int *dir, // direction for adding matches + Direction *dir, // direction for adding matches int dumpflags_arg // DUMPFLAG_* ) { @@ -6820,7 +6819,9 @@ spell_dump_compl ( // Dumps one word: apply case modifications and append a line to the buffer. // When "lnum" is zero add insert mode completion. -static void dump_word(slang_T *slang, char_u *word, char_u *pat, int *dir, int dumpflags, int wordflags, linenr_T lnum) +static void dump_word(slang_T *slang, char_u *word, char_u *pat, + Direction *dir, int dumpflags, int wordflags, + linenr_T lnum) { bool keepcap = false; char_u *p; @@ -6906,7 +6907,7 @@ dump_prefixes ( slang_T *slang, char_u *word, // case-folded word char_u *pat, - int *dir, + Direction *dir, int dumpflags, int flags, // flags with prefix ID linenr_T startlnum diff --git a/src/nvim/spell.h b/src/nvim/spell.h index ad66df4c5d..e93c82b91d 100644 --- a/src/nvim/spell.h +++ b/src/nvim/spell.h @@ -6,6 +6,7 @@ #include "nvim/spell_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/globals.h" +#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "spell.h.generated.h" diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index e91d560284..f99eca7953 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; ) @@ -4275,7 +4275,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing) * Everything that's left, up to the next command, should be the * filename to include. */ - eap->argt |= (XFILE | NOSPC); + eap->argt |= (EX_XFILE | EX_NOSPC); separate_nextcmd(eap); if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute(eap->arg)) { // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the @@ -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/runtest.vim b/src/nvim/testdir/runtest.vim index b02514143c..275edece1e 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -414,7 +414,7 @@ for s:test in sort(s:tests) " Repeat a flaky test. Give up when: " - it fails again with the same message - " - it fails five times (with a different mesage) + " - it fails five times (with a different message) if len(v:errors) > 0 \ && (index(s:flaky_tests, s:test) >= 0 \ || v:errors[0] =~ s:flaky_errors_re) diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index d032c9a739..fd9cfb54be 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -20,6 +20,7 @@ set tags=./tags,tags set undodir^=. set wildoptions= set startofline +set sessionoptions&vi " Prevent Nvim log from writing to stderr. let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok index 50fc5d2cef..9f283e808b 100644 --- a/src/nvim/testdir/test49.ok +++ b/src/nvim/testdir/test49.ok @@ -1,6 +1,4 @@ Results of test49.vim: -*** Test 16: OK (8722) -*** Test 17: OK (285127993) *** Test 18: OK (67224583) *** Test 19: OK (69275973) *** Test 20: OK (1874575085) @@ -8,16 +6,11 @@ Results of test49.vim: *** Test 22: OK (4161) *** Test 23: OK (49) *** Test 24: OK (41) -*** Test 25: OK (260177811) -*** Test 26: OK (1681500476) *** Test 27: OK (1996459) *** Test 28: OK (1996459) *** Test 29: OK (170428555) *** Test 30: OK (190905173) *** Test 31: OK (190905173) -*** Test 32: OK (354833067) ---- Test 33: sum = 178275600 (ok) -*** Test 33: OK (1216907538) *** Test 34: OK (2146584868) *** Test 35: OK (2146584868) *** Test 36: OK (1071644672) @@ -25,17 +18,8 @@ Results of test49.vim: *** Test 38: OK (357908480) *** Test 39: OK (357908480) *** Test 40: OK (357908480) -*** Test 41: OK (3076095) -*** Test 42: OK (1505155949) -*** Test 43: OK (1157763329) -*** Test 44: OK (1031761407) -*** Test 45: OK (1157763329) -*** Test 46: OK (739407) -*** Test 47: OK (371213935) -*** Test 48: OK (756255461) *** Test 49: OK (179000669) *** Test 50: OK (363550045) -*** Test 51: OK (40744667) *** Test 52: OK (1247112011) *** Test 53: OK (131071) *** Test 54: OK (2047) @@ -53,13 +37,6 @@ Results of test49.vim: *** Test 66: OK (5464) *** Test 67: OK (212514423) *** Test 68: OK (212514423) -*** Test 69: OK (8995471) -*** Test 70: OK (69544277) -*** Test 71: OK (34886997) -*** Test 72: OK (1789569365) -*** Test 73: OK (9032615) -*** Test 74: OK (224907669) -*** Test 75: OK (2000403408) *** Test 76: OK (1610087935) *** Test 77: OK (1388671) *** Test 78: OK (134217728) @@ -72,11 +49,6 @@ Results of test49.vim: *** Test 85: OK (198689) --- Test 86: No Crash for vimgrep on BufUnload *** Test 86: OK (0) ---- Test 87: 3 ---- Test 87: 5 ---- Test 87: abcdefghijk ---- Test 87: Successfully executed funcref Add2 -*** Test 87: OK (0) --- Test 88: All tests were run with throwing exceptions on error. The $VIMNOERRTHROW control is not configured. --- Test 88: All tests were run with throwing exceptions on interrupt. diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim index 5468f7c4aa..3ee5e9ff7c 100644 --- a/src/nvim/testdir/test49.vim +++ b/src/nvim/testdir/test49.vim @@ -1,6 +1,6 @@ " Vim script language tests " Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com> -" Last Change: 2019 Oct 08 +" Last Change: 2019 Nov 03 "------------------------------------------------------------------------------- " Test environment {{{1 @@ -521,7 +521,7 @@ endfunction " " Create a script that consists of the body of the function a:funcname. " Replace any ":return" by a ":finish", any argument variable by a global -" variable, and and every ":call" by a ":source" for the next following argument +" variable, and every ":call" by a ":source" for the next following argument " in the variable argument list. This function is useful if similar tests are " to be made for a ":return" from a function call or a ":finish" in a script " file. @@ -608,196 +608,8 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) " END_OF_TEST_ENVIRONMENT - do not change or remove this line. -" Tests 1 to 15 were moved to test_vimscript.vim -let Xtest = 16 - -"------------------------------------------------------------------------------- -" Test 16: Double :else or :elseif after :else {{{1 -" -" Multiple :elses or an :elseif after an :else are forbidden. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() abort - if 0 - Xpath 1 " X: 0 - else - Xpath 2 " X: 2 - else " aborts function - Xpath 4 " X: 0 - endif -endfunction - -function! G() abort - if 0 - Xpath 8 " X: 0 - else - Xpath 16 " X: 16 - elseif 1 " aborts function - Xpath 32 " X: 0 - else - Xpath 64 " X: 0 - endif -endfunction - -function! H() abort - if 0 - Xpath 128 " X: 0 - elseif 0 - Xpath 256 " X: 0 - else - Xpath 512 " X: 512 - else " aborts function - Xpath 1024 " X: 0 - endif -endfunction - -function! I() abort - if 0 - Xpath 2048 " X: 0 - elseif 0 - Xpath 4096 " X: 0 - else - Xpath 8192 " X: 8192 - elseif 1 " aborts function - Xpath 16384 " X: 0 - else - Xpath 32768 " X: 0 - endif -endfunction - -call F() -call G() -call H() -call I() - -delfunction F -delfunction G -delfunction H -delfunction I - -Xcheck 8722 - - -"------------------------------------------------------------------------------- -" Test 17: Nesting of unmatched :if or :endif inside a :while {{{1 -" -" The :while/:endwhile takes precedence in nesting over an unclosed -" :if or an unopened :endif. -"------------------------------------------------------------------------------- - -XpathINIT - -function! MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunction - -let messages = "" - -" While loops inside a function are continued on error. -function! F() - let v:errmsg = "" - XloopINIT 1 16 - let loops = 3 - while loops > 0 - let loops = loops - 1 " 2: 1: 0: - Xloop 1 " X: 1 + 1*16 + 1*16*16 - if (loops == 1) - Xloop 2 " X: 2*16 - XloopNEXT - continue - elseif (loops == 0) - Xloop 4 " X: 4*16*16 - break - elseif 1 - Xloop 8 " X: 8 - XloopNEXT - " endif missing! - endwhile " :endwhile after :if 1 - Xpath 4096 " X: 16*16*16 - if MSG('E171', "Missing :endif") - let g:messages = g:messages . "A" - endif - - let v:errmsg = "" - XloopINIT! 8192 4 - let loops = 2 - while loops > 0 " 2: 1: - XloopNEXT - let loops = loops - 1 - Xloop 1 " X: 8192 + 8192*4 - if 0 - Xloop 2 " X: 0 - " endif missing - endwhile " :endwhile after :if 0 - Xpath 131072 " X: 8192*4*4 - if MSG('E171', "Missing :endif") - let g:messages = g:messages . "B" - endif - - let v:errmsg = "" - XloopINIT 262144 4 - let loops = 2 - while loops > 0 " 2: 1: - let loops = loops - 1 - Xloop 1 " X: 262144 + 262144 * 4 - " if missing! - endif " :endif without :if in while - Xloop 2 " X: 524288 + 524288 * 4 - XloopNEXT - endwhile - Xpath 4194304 " X: 262144*4*4 - if MSG('E580', ":endif without :if") - let g:messages = g:messages . "C" - endif -endfunction - -call F() - -" Error continuation outside a function is at the outermost :endwhile or :endif. -let v:errmsg = "" -XloopINIT! 8388608 4 -let loops = 2 -while loops > 0 " 2: 1: - XloopNEXT - let loops = loops - 1 - Xloop 1 " X: 8388608 + 0 * 4 - if 0 - Xloop 2 " X: 0 - " endif missing! Following :endwhile fails. -endwhile | Xpath 134217728 " X: 0 -Xpath 268435456 " X: 2*8388608*4*4 -if MSG('E171', "Missing :endif") - let messages = g:messages . "D" -endif - -if messages != "ABCD" - Xpath 536870912 " X: 0 - Xout "messages is" messages "instead of ABCD" -endif - -unlet loops messages -delfunction F -delfunction MSG - -Xcheck 285127993 - +" Tests 1 to 17 were moved to test_vimscript.vim +let Xtest = 18 "------------------------------------------------------------------------------- " Test 18: Interrupt (Ctrl-C pressed) {{{1 @@ -1313,140 +1125,8 @@ endif Xcheck 41 - -"------------------------------------------------------------------------------- -" Test 25: Executing :finally clauses on normal control flow {{{1 -" -" Control flow in a :try conditional should always fall through to its -" :finally clause. A :finally clause of a :try conditional inside an -" inactive conditional should never be executed. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - let loops = 3 - XloopINIT 1 256 - while loops > 0 " 3: 2: 1: - Xloop 1 " X: 1 + 1*256 + 1*256*256 - if loops >= 2 - try - Xloop 2 " X: 2 + 2*256 - if loops == 2 - try - Xloop 4 " X: 4*256 - finally - Xloop 8 " X: 8*256 - endtry - endif - finally - Xloop 16 " X: 16 + 16*256 - if loops == 2 - try - Xloop 32 " X: 32*256 - finally - Xloop 64 " X: 64*256 - endtry - endif - endtry - endif - Xloop 128 " X: 128 + 128*256 + 128*256*256 - let loops = loops - 1 - XloopNEXT - endwhile - Xpath 16777216 " X: 16777216 -endfunction - -if 1 - try - Xpath 33554432 " X: 33554432 - call F() - Xpath 67108864 " X: 67108864 - finally - Xpath 134217728 " X: 134217728 - endtry -else - try - Xpath 268435456 " X: 0 - finally - Xpath 536870912 " X: 0 - endtry -endif - -delfunction F - -Xcheck 260177811 - - -"------------------------------------------------------------------------------- -" Test 26: Executing :finally clauses after :continue or :break {{{1 -" -" For a :continue or :break dynamically enclosed in a :try/:endtry -" region inside the next surrounding :while/:endwhile, if the -" :continue/:break is before the :finally, the :finally clause is -" executed first. If the :continue/:break is after the :finally, the -" :finally clause is broken (like an :if/:endif region). -"------------------------------------------------------------------------------- - -XpathINIT - -try - let loops = 3 - XloopINIT! 1 32 - while loops > 0 - XloopNEXT - try - try - if loops == 2 " 3: 2: 1: - Xloop 1 " X: 1*32 - let loops = loops - 1 - continue - elseif loops == 1 - Xloop 2 " X: 2*32*32 - break - finish - endif - Xloop 4 " X: 4 - endtry - finally - Xloop 8 " X: 8 + 8*32 + 8*32*32 - endtry - Xloop 16 " X: 16 - let loops = loops - 1 - endwhile - Xpath 32768 " X: 32768 -finally - Xpath 65536 " X: 65536 - let loops = 3 - XloopINIT 131072 16 - while loops > 0 - try - finally - try - if loops == 2 - Xloop 1 " X: 131072*16 - let loops = loops - 1 - XloopNEXT - continue - elseif loops == 1 - Xloop 2 " X: 131072*2*16*16 - break - finish - endif - endtry - Xloop 4 " X: 131072*4 - endtry - Xloop 8 " X: 131072*8 - let loops = loops - 1 - XloopNEXT - endwhile - Xpath 536870912 " X: 536870912 -endtry -Xpath 1073741824 " X: 1073741824 - -unlet loops - -Xcheck 1681500476 +" Tests 25 and 26 were moved to test_trycatch.vim +let Xtest = 27 "------------------------------------------------------------------------------- @@ -1783,252 +1463,8 @@ endif Xcheck 190905173 - -"------------------------------------------------------------------------------- -" Test 32: Remembering the :return value on :finally {{{1 -" -" If a :finally clause is executed due to a :return specifying -" a value, this is the value visible to the caller if not overwritten -" by a new :return in the :finally clause. A :return without a value -" in the :finally clause overwrites with value 0. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - try - Xpath 1 " X: 1 - try - Xpath 2 " X: 2 - return "ABCD" - Xpath 4 " X: 0 - finally - Xpath 8 " X: 8 - endtry - Xpath 16 " X: 0 - finally - Xpath 32 " X: 32 - endtry - Xpath 64 " X: 0 -endfunction - -function! G() - try - Xpath 128 " X: 128 - return 8 - Xpath 256 " X: 0 - finally - Xpath 512 " X: 512 - return 16 + strlen(F()) - Xpath 1024 " X: 0 - endtry - Xpath 2048 " X: 0 -endfunction - -function! H() - try - Xpath 4096 " X: 4096 - return 32 - Xpath 8192 " X: 0 - finally - Xpath 16384 " X: 16384 - return - Xpath 32768 " X: 0 - endtry - Xpath 65536 " X: 0 -endfunction - -function! I() - try - Xpath 131072 " X: 131072 - finally - Xpath 262144 " X: 262144 - return G() + H() + 64 - Xpath 524288 " X: 0 - endtry - Xpath 1048576 " X: 0 -endfunction - -let retcode = I() -Xpath 2097152 " X: 2097152 - -if retcode < 0 - Xpath 4194304 " X: 0 -endif -if retcode % 4 - Xpath 8388608 " X: 0 -endif -if (retcode/4) % 2 - Xpath 16777216 " X: 16777216 -endif -if (retcode/8) % 2 - Xpath 33554432 " X: 0 -endif -if (retcode/16) % 2 - Xpath 67108864 " X: 67108864 -endif -if (retcode/32) % 2 - Xpath 134217728 " X: 0 -endif -if (retcode/64) % 2 - Xpath 268435456 " X: 268435456 -endif -if retcode/128 - Xpath 536870912 " X: 0 -endif - -unlet retcode -delfunction F -delfunction G -delfunction H -delfunction I - -Xcheck 354833067 - - -"------------------------------------------------------------------------------- -" Test 33: :return under :execute or user command and :finally {{{1 -" -" A :return command may be executed under an ":execute" or from -" a user command. Executing of :finally clauses and passing through -" the return code works also then. -"------------------------------------------------------------------------------- -XpathINIT - -command! -nargs=? RETURN - \ try | return <args> | finally | return <args> * 2 | endtry - -function! F() - try - RETURN 8 - Xpath 1 " X: 0 - finally - Xpath 2 " X: 2 - endtry - Xpath 4 " X: 0 -endfunction - -function! G() - try - RETURN 32 - Xpath 8 " X: 0 - finally - Xpath 16 " X: 16 - RETURN 128 - Xpath 32 " X: 0 - endtry - Xpath 64 " X: 0 -endfunction - -function! H() - try - execute "try | return 512 | finally | return 1024 | endtry" - Xpath 128 " X: 0 - finally - Xpath 256 " X: 256 - endtry - Xpath 512 " X: 0 -endfunction - -function! I() - try - execute "try | return 2048 | finally | return 4096 | endtry" - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - execute "try | return 8192 | finally | return 16384 | endtry" - Xpath 4096 " X: 0 - endtry - Xpath 8192 " X: 0 -endfunction - -function! J() - try - RETURN 32768 - Xpath 16384 " X: 0 - finally - Xpath 32768 " X: 32768 - return - Xpath 65536 " X: 0 - endtry - Xpath 131072 " X: 0 -endfunction - -function! K() - try - execute "try | return 131072 | finally | return 262144 | endtry" - Xpath 262144 " X: 0 - finally - Xpath 524288 " X: 524288 - execute "try | return 524288 | finally | return | endtry" - Xpath 1048576 " X: 0 - endtry - Xpath 2097152 " X: 0 -endfunction - -function! L() - try - return - Xpath 4194304 " X: 0 - finally - Xpath 8388608 " X: 8388608 - RETURN 1048576 - Xpath 16777216 " X: 0 - endtry - Xpath 33554432 " X: 0 -endfunction - -function! M() - try - return - Xpath 67108864 " X: 0 - finally - Xpath 134217728 " X: 134217728 - execute "try | return 4194304 | finally | return 8388608 | endtry" - Xpath 268435456 " X: 0 - endtry - Xpath 536870912 " X: 0 -endfunction - -function! N() - RETURN 16777216 -endfunction - -function! O() - execute "try | return 67108864 | finally | return 134217728 | endtry" -endfunction - -let sum = F() + G() + H() + I() + J() + K() + L() + M() -let expected = 16 + 256 + 1024 + 16384 + 0 + 0 + 2097152 + 8388608 -let sum = sum + N() + O() -let expected = expected + 33554432 + 134217728 - -if sum == expected - Xout "sum = " . sum . " (ok)" -else - Xout "sum = " . sum . ", expected: " . expected -endif - -Xpath 1073741824 " X: 1073741824 - -if sum != expected - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 -endif - -unlet sum expected -delfunction F -delfunction G -delfunction H -delfunction I -delfunction J -delfunction K -delfunction L -delfunction M -delfunction N -delfunction O - -Xcheck 1216907538 +" Tests 32 and 33 were moved to test_trycatch.vim +let Xtest = 34 "------------------------------------------------------------------------------- @@ -2654,643 +2090,8 @@ endif Xcheck 357908480 - -"------------------------------------------------------------------------------- -" Test 41: Skipped :throw finding next command {{{1 -" -" A :throw in an inactive conditional must not hide a following -" command. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - Xpath 1 " X: 1 - if 0 | throw "never" | endif | Xpath 2 " X: 2 - Xpath 4 " X: 4 -endfunction - -function! G() - Xpath 8 " X: 8 - while 0 | throw "never" | endwhile | Xpath 16 " X: 16 - Xpath 32 " X: 32 -endfunction - -function H() - Xpath 64 " X: 64 - if 0 | try | throw "never" | endtry | endif | Xpath 128 " X: 128 - Xpath 256 " X: 256 -endfunction - -Xpath 512 " X: 512 - -try - Xpath 1024 " X: 1024 - call F() - Xpath 2048 " X: 2048 -catch /.*/ - Xpath 4096 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 8192 " X: 8192 - -try - Xpath 16384 " X: 16384 - call G() - Xpath 32768 " X: 32768 -catch /.*/ - Xpath 65536 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 131072 " X: 131072 - -try - Xpath 262144 " X: 262144 - call H() - Xpath 524288 " X: 524288 -catch /.*/ - Xpath 1048576 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 2097152 " X: 2097152 - -delfunction F -delfunction G -delfunction H - -Xcheck 3076095 - - -"------------------------------------------------------------------------------- -" Test 42: Catching number and string exceptions {{{1 -" -" When a number is thrown, it is converted to a string exception. -" Numbers and strings may be caught by specifying a regular exception -" as argument to the :catch command. -"------------------------------------------------------------------------------- - -XpathINIT - -try - - try - Xpath 1 " X: 1 - throw 4711 - Xpath 2 " X: 0 - catch /4711/ - Xpath 4 " X: 4 - endtry - - try - Xpath 8 " X: 8 - throw 4711 - Xpath 16 " X: 0 - catch /^4711$/ - Xpath 32 " X: 32 - endtry - - try - Xpath 64 " X: 64 - throw 4711 - Xpath 128 " X: 0 - catch /\d/ - Xpath 256 " X: 256 - endtry - - try - Xpath 512 " X: 512 - throw 4711 - Xpath 1024 " X: 0 - catch /^\d\+$/ - Xpath 2048 " X: 2048 - endtry - - try - Xpath 4096 " X: 4096 - throw "arrgh" - Xpath 8192 " X: 0 - catch /arrgh/ - Xpath 16384 " X: 16384 - endtry - - try - Xpath 32768 " X: 32768 - throw "arrgh" - Xpath 65536 " X: 0 - catch /^arrgh$/ - Xpath 131072 " X: 131072 - endtry - - try - Xpath 262144 " X: 262144 - throw "arrgh" - Xpath 524288 " X: 0 - catch /\l/ - Xpath 1048576 " X: 1048576 - endtry - - try - Xpath 2097152 " X: 2097152 - throw "arrgh" - Xpath 4194304 " X: 0 - catch /^\l\+$/ - Xpath 8388608 " X: 8388608 - endtry - - try - try - Xpath 16777216 " X: 16777216 - throw "ARRGH" - Xpath 33554432 " X: 0 - catch /^arrgh$/ - Xpath 67108864 " X: 0 - endtry - catch /^\carrgh$/ - Xpath 134217728 " X: 134217728 - endtry - - try - Xpath 268435456 " X: 268435456 - throw "" - Xpath 536870912 " X: 0 - catch /^$/ - Xpath 1073741824 " X: 1073741824 - endtry - -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xcheck 1505155949 - - -"------------------------------------------------------------------------------- -" Test 43: Selecting the correct :catch clause {{{1 -" -" When an exception is thrown and there are multiple :catch clauses, -" the first matching one is taken. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT 1 1024 -let loops = 3 -while loops > 0 - try - if loops == 3 - Xloop 1 " X: 1 - throw "a" - Xloop 2 " X: 0 - elseif loops == 2 - Xloop 4 " X: 4*1024 - throw "ab" - Xloop 8 " X: 0 - elseif loops == 1 - Xloop 16 " X: 16*1024*1024 - throw "abc" - Xloop 32 " X: 0 - endif - catch /abc/ - Xloop 64 " X: 64*1024*1024 - catch /ab/ - Xloop 128 " X: 128*1024 - catch /.*/ - Xloop 256 " X: 256 - catch /a/ - Xloop 512 " X: 0 - endtry - - let loops = loops - 1 - XloopNEXT -endwhile -Xpath 1073741824 " X: 1073741824 - -unlet loops - -Xcheck 1157763329 - - -"------------------------------------------------------------------------------- -" Test 44: Missing or empty :catch patterns {{{1 -" -" A missing or empty :catch pattern means the same as /.*/, that is, -" catches everything. To catch only empty exceptions, /^$/ must be -" used. A :catch with missing, empty, or /.*/ argument also works -" when followed by another command separated by a bar on the same -" line. :catch patterns cannot be specified between ||. But other -" pattern separators can be used instead of //. -"------------------------------------------------------------------------------- - -XpathINIT - -try - try - Xpath 1 " X: 1 - throw "" - catch /^$/ - Xpath 2 " X: 2 - endtry - - try - Xpath 4 " X: 4 - throw "" - catch /.*/ - Xpath 8 " X: 8 - endtry - - try - Xpath 16 " X: 16 - throw "" - catch // - Xpath 32 " X: 32 - endtry - - try - Xpath 64 " X: 64 - throw "" - catch - Xpath 128 " X: 128 - endtry - - try - Xpath 256 " X: 256 - throw "oops" - catch /^$/ - Xpath 512 " X: 0 - catch /.*/ - Xpath 1024 " X: 1024 - endtry - - try - Xpath 2048 " X: 2048 - throw "arrgh" - catch /^$/ - Xpath 4096 " X: 0 - catch // - Xpath 8192 " X: 8192 - endtry - - try - Xpath 16384 " X: 16384 - throw "brrr" - catch /^$/ - Xpath 32768 " X: 0 - catch - Xpath 65536 " X: 65536 - endtry - - try | Xpath 131072 | throw "x" | catch /.*/ | Xpath 262144 | endtry - " X: 131072 + 262144 - - try | Xpath 524288 | throw "y" | catch // | Xpath 1048576 | endtry - " X: 524288 + 1048576 - - while 1 - try - let caught = 0 - let v:errmsg = "" - " Extra try level: if ":catch" without arguments below raises - " a syntax error because it misinterprets the "Xpath" as a pattern, - " let it be caught by the ":catch /.*/" below. - try - try | Xpath 2097152 | throw "z" | catch | Xpath 4194304 | : - endtry " X: 2097152 + 4194304 - endtry - catch /.*/ - let caught = 1 - Xout v:exception "in" v:throwpoint - finally - if $VIMNOERRTHROW && v:errmsg != "" - Xout v:errmsg - endif - if caught || $VIMNOERRTHROW && v:errmsg != "" - Xpath 8388608 " X: 0 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - let cologne = 4711 - try - try - Xpath 16777216 " X: 16777216 - throw "throw cologne" - " Next lines catches all and throws 4711: - catch |throw cologne| - Xpath 33554432 " X: 0 - endtry - catch /4711/ - Xpath 67108864 " X: 67108864 - endtry - - try - Xpath 134217728 " X: 134217728 - throw "plus" - catch +plus+ - Xpath 268435456 " X: 268435456 - endtry - - Xpath 536870912 " X: 536870912 -catch /.*/ - Xpath 1073741824 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -unlet! caught cologne - -Xcheck 1031761407 - - -"------------------------------------------------------------------------------- -" Test 45: Catching exceptions from nested :try blocks {{{1 -" -" When :try blocks are nested, an exception is caught by the innermost -" try conditional that has a matching :catch clause. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT 1 1024 -let loops = 3 -while loops > 0 - try - try - try - try - if loops == 3 - Xloop 1 " X: 1 - throw "a" - Xloop 2 " X: 0 - elseif loops == 2 - Xloop 4 " X: 4*1024 - throw "ab" - Xloop 8 " X: 0 - elseif loops == 1 - Xloop 16 " X: 16*1024*1024 - throw "abc" - Xloop 32 " X: 0 - endif - catch /abc/ - Xloop 64 " X: 64*1024*1024 - endtry - catch /ab/ - Xloop 128 " X: 128*1024 - endtry - catch /.*/ - Xloop 256 " X: 256 - endtry - catch /a/ - Xloop 512 " X: 0 - endtry - - let loops = loops - 1 - XloopNEXT -endwhile -Xpath 1073741824 " X: 1073741824 - -unlet loops - -Xcheck 1157763329 - - -"------------------------------------------------------------------------------- -" Test 46: Executing :finally after a :throw in nested :try {{{1 -" -" When an exception is thrown from within nested :try blocks, the -" :finally clauses of the non-catching try conditionals should be -" executed before the matching :catch of the next surrounding :try -" gets the control. If this also has a :finally clause, it is -" executed afterwards. -"------------------------------------------------------------------------------- - -XpathINIT - -let sum = 0 - -try - Xpath 1 " X: 1 - try - Xpath 2 " X: 2 - try - Xpath 4 " X: 4 - try - Xpath 8 " X: 8 - throw "ABC" - Xpath 16 " X: 0 - catch /xyz/ - Xpath 32 " X: 0 - finally - Xpath 64 " X: 64 - if sum != 0 - Xpath 128 " X: 0 - endif - let sum = sum + 1 - endtry - Xpath 256 " X: 0 - catch /123/ - Xpath 512 " X: 0 - catch /321/ - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - if sum != 1 - Xpath 4096 " X: 0 - endif - let sum = sum + 2 - endtry - Xpath 8192 " X: 0 - finally - Xpath 16384 " X: 16384 - if sum != 3 - Xpath 32768 " X: 0 - endif - let sum = sum + 4 - endtry - Xpath 65536 " X: 0 -catch /ABC/ - Xpath 131072 " X: 131072 - if sum != 7 - Xpath 262144 " X: 0 - endif - let sum = sum + 8 -finally - Xpath 524288 " X: 524288 - if sum != 15 - Xpath 1048576 " X: 0 - endif - let sum = sum + 16 -endtry -Xpath 65536 " X: 65536 -if sum != 31 - Xpath 131072 " X: 0 -endif - -unlet sum - -Xcheck 739407 - - -"------------------------------------------------------------------------------- -" Test 47: Throwing exceptions from a :catch clause {{{1 -" -" When an exception is thrown from a :catch clause, it should not be -" caught by a :catch of the same :try conditional. After executing -" the :finally clause (if present), surrounding try conditionals -" should be checked for a matching :catch. -"------------------------------------------------------------------------------- - -XpathINIT - -Xpath 1 " X: 1 -try - Xpath 2 " X: 2 - try - Xpath 4 " X: 4 - try - Xpath 8 " X: 8 - throw "x1" - Xpath 16 " X: 0 - catch /x1/ - Xpath 32 " X: 32 - try - Xpath 64 " X: 64 - throw "x2" - Xpath 128 " X: 0 - catch /x1/ - Xpath 256 " X: 0 - catch /x2/ - Xpath 512 " X: 512 - try - Xpath 1024 " X: 1024 - throw "x3" - Xpath 2048 " X: 0 - catch /x1/ - Xpath 4096 " X: 0 - catch /x2/ - Xpath 8192 " X: 0 - finally - Xpath 16384 " X: 16384 - endtry - Xpath 32768 " X: 0 - catch /x3/ - Xpath 65536 " X: 0 - endtry - Xpath 131072 " X: 0 - catch /x1/ - Xpath 262144 " X: 0 - catch /x2/ - Xpath 524288 " X: 0 - catch /x3/ - Xpath 1048576 " X: 0 - finally - Xpath 2097152 " X: 2097152 - endtry - Xpath 4194304 " X: 0 - catch /x1/ - Xpath 8388608 " X: 0 - catch /x2/ - Xpath 16777216 " X: 0 - catch /x3/ - Xpath 33554432 " X: 33554432 - endtry - Xpath 67108864 " X: 67108864 -catch /.*/ - Xpath 134217728 " X: 0 - Xout v:exception "in" v:throwpoint -endtry -Xpath 268435456 " X: 268435456 - -Xcheck 371213935 - - -"------------------------------------------------------------------------------- -" Test 48: Throwing exceptions from a :finally clause {{{1 -" -" When an exception is thrown from a :finally clause, it should not be -" caught by a :catch of the same :try conditional. Surrounding try -" conditionals should be checked for a matching :catch. A previously -" thrown exception is discarded. -"------------------------------------------------------------------------------- - -XpathINIT - -try - - try - try - Xpath 1 " X: 1 - catch /x1/ - Xpath 2 " X: 0 - finally - Xpath 4 " X: 4 - throw "x1" - Xpath 8 " X: 0 - endtry - Xpath 16 " X: 0 - catch /x1/ - Xpath 32 " X: 32 - endtry - Xpath 64 " X: 64 - - try - try - Xpath 128 " X: 128 - throw "x2" - Xpath 256 " X: 0 - catch /x2/ - Xpath 512 " X: 512 - catch /x3/ - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - throw "x3" - Xpath 4096 " X: 0 - endtry - Xpath 8192 " X: 0 - catch /x2/ - Xpath 16384 " X: 0 - catch /x3/ - Xpath 32768 " X: 32768 - endtry - Xpath 65536 " X: 65536 - - try - try - try - Xpath 131072 " X: 131072 - throw "x4" - Xpath 262144 " X: 0 - catch /x5/ - Xpath 524288 " X: 0 - finally - Xpath 1048576 " X: 1048576 - throw "x5" " discards "x4" - Xpath 2097152 " X: 0 - endtry - Xpath 4194304 " X: 0 - catch /x4/ - Xpath 8388608 " X: 0 - finally - Xpath 16777216 " X: 16777216 - endtry - Xpath 33554432 " X: 0 - catch /x5/ - Xpath 67108864 " X: 67108864 - endtry - Xpath 134217728 " X: 134217728 - -catch /.*/ - Xpath 268435456 " X: 0 - Xout v:exception "in" v:throwpoint -endtry -Xpath 536870912 " X: 536870912 - -Xcheck 756255461 +" Tests 41 to 48 were moved to test_trycatch.vim +let Xtest = 49 "------------------------------------------------------------------------------- @@ -3443,124 +2244,8 @@ delfunction F Xcheck 363550045 - -"------------------------------------------------------------------------------- -" Test 51: Throwing exceptions across :execute and user commands {{{1 -" -" A :throw command may be executed under an ":execute" or from -" a user command. -"------------------------------------------------------------------------------- - -XpathINIT - -command! -nargs=? THROW1 throw <args> | throw 1 -command! -nargs=? THROW2 try | throw <args> | endtry | throw 2 -command! -nargs=? THROW3 try | throw 3 | catch /3/ | throw <args> | endtry -command! -nargs=? THROW4 try | throw 4 | finally | throw <args> | endtry - -try - - try - try - Xpath 1 " X: 1 - THROW1 "A" - catch /A/ - Xpath 2 " X: 2 - endtry - catch /1/ - Xpath 4 " X: 0 - endtry - - try - try - Xpath 8 " X: 8 - THROW2 "B" - catch /B/ - Xpath 16 " X: 16 - endtry - catch /2/ - Xpath 32 " X: 0 - endtry - - try - try - Xpath 64 " X: 64 - THROW3 "C" - catch /C/ - Xpath 128 " X: 128 - endtry - catch /3/ - Xpath 256 " X: 0 - endtry - - try - try - Xpath 512 " X: 512 - THROW4 "D" - catch /D/ - Xpath 1024 " X: 1024 - endtry - catch /4/ - Xpath 2048 " X: 0 - endtry - - try - try - Xpath 4096 " X: 4096 - execute 'throw "E" | throw 5' - catch /E/ - Xpath 8192 " X: 8192 - endtry - catch /5/ - Xpath 16384 " X: 0 - endtry - - try - try - Xpath 32768 " X: 32768 - execute 'try | throw "F" | endtry | throw 6' - catch /F/ - Xpath 65536 " X: 65536 - endtry - catch /6/ - Xpath 131072 " X: 0 - endtry - - try - try - Xpath 262144 " X: 262144 - execute'try | throw 7 | catch /7/ | throw "G" | endtry' - catch /G/ - Xpath 524288 " X: 524288 - endtry - catch /7/ - Xpath 1048576 " X: 0 - endtry - - try - try - Xpath 2097152 " X: 2097152 - execute 'try | throw 8 | finally | throw "H" | endtry' - catch /H/ - Xpath 4194304 " X: 4194304 - endtry - catch /8/ - Xpath 8388608 " X: 0 - endtry - -catch /.*/ - Xpath 16777216 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 33554432 " X: 33554432 - -delcommand THROW1 -delcommand THROW2 -delcommand THROW3 -delcommand THROW4 - -Xcheck 40744667 +" Test 51 was moved to test_trycatch.vim +let Xtest = 52 "------------------------------------------------------------------------------- @@ -3694,7 +2379,7 @@ endif if ExtraVim(msgfile) try Xpath 4194304 " X: 4194304 - let x = novar " error E121; exception: E121 + let x = novar " error E121/E15; exception: E121 catch /E15:/ " should not catch Xpath 8388608 " X: 0 endtry @@ -3702,7 +2387,7 @@ if ExtraVim(msgfile) endif Xpath 33554432 " X: 33554432 -if !MESSAGES('E121', "Undefined variable") +if !MESSAGES('E121', "Undefined variable", 'E15', "Invalid expression") Xpath 67108864 " X: 0 endif @@ -4236,14 +2921,18 @@ if MESSAGES('E170', "Missing :endwhile") endif if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" if 1 -" endtry + try + Xpath 4194304 " X: 4194304 + let x = novar " error E121; exception: E121 + catch /E15:/ " should not catch + Xpath 8388608 " X: 0 + endtry + Xpath 16777216 " X: 0 endif -if MESSAGES('E171', "Missing :endif") - Xpath 128 " X: 128 + +Xpath 33554432 " X: 33554432 +if !MESSAGES('E121', "Undefined variable") + Xpath 67108864 " X: 0 endif if ExtraVim(msgfile) @@ -6227,954 +4916,8 @@ delfunction F Xcheck 212514423 - -"------------------------------------------------------------------------------- -" Test 69: :throw across :if, :elseif, :while {{{1 -" -" On an :if, :elseif, or :while command, an exception might be thrown -" during evaluation of the expression to test. The exception can be -" caught by the script. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT! 1 2 - -function! THROW(x) - XloopNEXT - Xloop 1 " X: 1 + 2 + 4 - throw a:x -endfunction - -try - - try - Xpath 8 " X: 8 - if 4711 == THROW("if") + 111 - Xpath 16 " X: 0 - else - Xpath 32 " X: 0 - endif - Xpath 64 " X: 0 - catch /^if$/ - Xpath 128 " X: 128 - catch /.*/ - Xpath 256 " X: 0 - Xout "if:" v:exception "in" v:throwpoint - endtry - - try - Xpath 512 " X: 512 - if 4711 == 4 + 7 + 1 + 1 - Xpath 1024 " X: 0 - elseif 4711 == THROW("elseif") + 222 - Xpath 2048 " X: 0 - else - Xpath 4096 " X: 0 - endif - Xpath 8192 " X: 0 - catch /^elseif$/ - Xpath 16384 " X: 16384 - catch /.*/ - Xpath 32768 " X: 0 - Xout "elseif:" v:exception "in" v:throwpoint - endtry - - try - Xpath 65536 " X: 65536 - while 4711 == THROW("while") + 4711 - Xpath 131072 " X: 0 - break - endwhile - Xpath 262144 " X: 0 - catch /^while$/ - Xpath 524288 " X: 524288 - catch /.*/ - Xpath 1048576 " X: 0 - Xout "while:" v:exception "in" v:throwpoint - endtry - -catch /^0$/ " default return value - Xpath 2097152 " X: 0 - Xout v:throwpoint -catch /.*/ - Xout v:exception "in" v:throwpoint - Xpath 4194304 " X: 0 -endtry - -Xpath 8388608 " X: 8388608 - -delfunction THROW - -Xcheck 8995471 - - -"------------------------------------------------------------------------------- -" Test 70: :throw across :return or :throw {{{1 -" -" On a :return or :throw command, an exception might be thrown during -" evaluation of the expression to return or throw, respectively. The -" exception can be caught by the script. -"------------------------------------------------------------------------------- - -XpathINIT - -let taken = "" - -function! THROW(x, n) - let g:taken = g:taken . "T" . a:n - throw a:x -endfunction - -function! F(x, y, n) - let g:taken = g:taken . "F" . a:n - return a:x + THROW(a:y, a:n) -endfunction - -function! G(x, y, n) - let g:taken = g:taken . "G" . a:n - throw a:x . THROW(a:y, a:n) - return a:x -endfunction - -try - try - Xpath 1 " X: 1 - call F(4711, "return", 1) - Xpath 2 " X: 0 - catch /^return$/ - Xpath 4 " X: 4 - catch /.*/ - Xpath 8 " X: 0 - Xout "return:" v:exception "in" v:throwpoint - endtry - - try - Xpath 16 " X: 16 - let var = F(4712, "return-var", 2) - Xpath 32 " X: 0 - catch /^return-var$/ - Xpath 64 " X: 64 - catch /.*/ - Xpath 128 " X: 0 - Xout "return-var:" v:exception "in" v:throwpoint - finally - unlet! var - endtry - - try - Xpath 256 " X: 256 - throw "except1" . THROW("throw1", 3) - Xpath 512 " X: 0 - catch /^except1/ - Xpath 1024 " X: 0 - catch /^throw1$/ - Xpath 2048 " X: 2048 - catch /.*/ - Xpath 4096 " X: 0 - Xout "throw1:" v:exception "in" v:throwpoint - endtry - - try - Xpath 8192 " X: 8192 - call G("except2", "throw2", 4) - Xpath 16384 " X: 0 - catch /^except2/ - Xpath 32768 " X: 0 - catch /^throw2$/ - Xpath 65536 " X: 65536 - catch /.*/ - Xpath 131072 " X: 0 - Xout "throw2:" v:exception "in" v:throwpoint - endtry - - try - Xpath 262144 " X: 262144 - let var = G("except3", "throw3", 5) - Xpath 524288 " X: 0 - catch /^except3/ - Xpath 1048576 " X: 0 - catch /^throw3$/ - Xpath 2097152 " X: 2097152 - catch /.*/ - Xpath 4194304 " X: 0 - Xout "throw3:" v:exception "in" v:throwpoint - finally - unlet! var - endtry - - let expected = "F1T1F2T2T3G4T4G5T5" - if taken != expected - Xpath 8388608 " X: 0 - Xout "'taken' is" taken "instead of" expected - endif - -catch /^0$/ " default return value - Xpath 16777216 " X: 0 - Xout v:throwpoint -catch /.*/ - Xpath 33554432 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 67108864 " X: 67108864 - -unlet taken expected -delfunction THROW -delfunction F -delfunction G - -Xcheck 69544277 - - -"------------------------------------------------------------------------------- -" Test 71: :throw across :echo variants and :execute {{{1 -" -" On an :echo, :echon, :echomsg, :echoerr, or :execute command, an -" exception might be thrown during evaluation of the arguments to -" be displayed or executed as a command, respectively. Any following -" arguments are not evaluated, then. The exception can be caught by -" the script. -"------------------------------------------------------------------------------- - -XpathINIT - -let taken = "" - -function! THROW(x, n) - let g:taken = g:taken . "T" . a:n - throw a:x -endfunction - -function! F(n) - let g:taken = g:taken . "F" . a:n - return "F" . a:n -endfunction - -try - try - Xpath 1 " X: 1 - echo "echo" . THROW("echo-except", 1) F(1) - Xpath 2 " X: 0 - catch /^echo-except$/ - Xpath 4 " X: 4 - catch /.*/ - Xpath 8 " X: 0 - Xout "echo:" v:exception "in" v:throwpoint - endtry - - try - Xpath 16 " X: 16 - echon "echon" . THROW("echon-except", 2) F(2) - Xpath 32 " X: 0 - catch /^echon-except$/ - Xpath 64 " X: 64 - catch /.*/ - Xpath 128 " X: 0 - Xout "echon:" v:exception "in" v:throwpoint - endtry - - try - Xpath 256 " X: 256 - echomsg "echomsg" . THROW("echomsg-except", 3) F(3) - Xpath 512 " X: 0 - catch /^echomsg-except$/ - Xpath 1024 " X: 1024 - catch /.*/ - Xpath 2048 " X: 0 - Xout "echomsg:" v:exception "in" v:throwpoint - endtry - - try - Xpath 4096 " X: 4096 - echoerr "echoerr" . THROW("echoerr-except", 4) F(4) - Xpath 8192 " X: 0 - catch /^echoerr-except$/ - Xpath 16384 " X: 16384 - catch /Vim/ - Xpath 32768 " X: 0 - catch /echoerr/ - Xpath 65536 " X: 0 - catch /.*/ - Xpath 131072 " X: 0 - Xout "echoerr:" v:exception "in" v:throwpoint - endtry - - try - Xpath 262144 " X: 262144 - execute "echo 'execute" . THROW("execute-except", 5) F(5) "'" - Xpath 524288 " X: 0 - catch /^execute-except$/ - Xpath 1048576 " X: 1048576 - catch /.*/ - Xpath 2097152 " X: 0 - Xout "execute:" v:exception "in" v:throwpoint - endtry - - let expected = "T1T2T3T4T5" - if taken != expected - Xpath 4194304 " X: 0 - Xout "'taken' is" taken "instead of" expected - endif - -catch /^0$/ " default return value - Xpath 8388608 " X: 0 - Xout v:throwpoint -catch /.*/ - Xpath 16777216 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 33554432 " X: 33554432 - -unlet taken expected -delfunction THROW -delfunction F - -Xcheck 34886997 - - -"------------------------------------------------------------------------------- -" Test 72: :throw across :let or :unlet {{{1 -" -" On a :let command, an exception might be thrown during evaluation -" of the expression to assign. On an :let or :unlet command, the -" evaluation of the name of the variable to be assigned or list or -" deleted, respectively, may throw an exception. Any following -" arguments are not evaluated, then. The exception can be caught by -" the script. -"------------------------------------------------------------------------------- - -XpathINIT - -let throwcount = 0 - -function! THROW(x) - let g:throwcount = g:throwcount + 1 - throw a:x -endfunction - -try - try - let $VAR = "old_value" - Xpath 1 " X: 1 - let $VAR = "let(" . THROW("var") . ")" - Xpath 2 " X: 0 - catch /^var$/ - Xpath 4 " X: 4 - finally - if $VAR != "old_value" - Xpath 8 " X: 0 - endif - endtry - - try - let @a = "old_value" - Xpath 16 " X: 16 - let @a = "let(" . THROW("reg") . ")" - Xpath 32 " X: 0 - catch /^reg$/ - try - Xpath 64 " X: 64 - let @A = "let(" . THROW("REG") . ")" - Xpath 128 " X: 0 - catch /^REG$/ - Xpath 256 " X: 256 - endtry - finally - if @a != "old_value" - Xpath 512 " X: 0 - endif - if @A != "old_value" - Xpath 1024 " X: 0 - endif - endtry - - try - let saved_gpath = &g:path - let saved_lpath = &l:path - Xpath 2048 " X: 2048 - let &path = "let(" . THROW("opt") . ")" - Xpath 4096 " X: 0 - catch /^opt$/ - try - Xpath 8192 " X: 8192 - let &g:path = "let(" . THROW("gopt") . ")" - Xpath 16384 " X: 0 - catch /^gopt$/ - try - Xpath 32768 " X: 32768 - let &l:path = "let(" . THROW("lopt") . ")" - Xpath 65536 " X: 0 - catch /^lopt$/ - Xpath 131072 " X: 131072 - endtry - endtry - finally - if &g:path != saved_gpath || &l:path != saved_lpath - Xpath 262144 " X: 0 - endif - let &g:path = saved_gpath - let &l:path = saved_lpath - endtry - - unlet! var1 var2 var3 - - try - Xpath 524288 " X: 524288 - let var1 = "let(" . THROW("var1") . ")" - Xpath 1048576 " X: 0 - catch /^var1$/ - Xpath 2097152 " X: 2097152 - finally - if exists("var1") - Xpath 4194304 " X: 0 - endif - endtry - - try - let var2 = "old_value" - Xpath 8388608 " X: 8388608 - let var2 = "let(" . THROW("var2"). ")" - Xpath 16777216 " X: 0 - catch /^var2$/ - Xpath 33554432 " X: 33554432 - finally - if var2 != "old_value" - Xpath 67108864 " X: 0 - endif - endtry - - try - Xpath 134217728 " X: 134217728 - let var{THROW("var3")} = 4711 - Xpath 268435456 " X: 0 - catch /^var3$/ - Xpath 536870912 " X: 536870912 - endtry - - let addpath = "" - - function ADDPATH(p) - let g:addpath = g:addpath . a:p - endfunction - - try - call ADDPATH("T1") - let var{THROW("var4")} var{ADDPATH("T2")} | call ADDPATH("T3") - call ADDPATH("T4") - catch /^var4$/ - call ADDPATH("T5") - endtry - - try - call ADDPATH("T6") - unlet var{THROW("var5")} var{ADDPATH("T7")} | call ADDPATH("T8") - call ADDPATH("T9") - catch /^var5$/ - call ADDPATH("T10") - endtry - - if addpath != "T1T5T6T10" || throwcount != 11 - throw "addpath: " . addpath . ", throwcount: " . throwcount - endif - - Xpath 1073741824 " X: 1073741824 - -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -unlet! var1 var2 var3 addpath throwcount -delfunction THROW - -Xcheck 1789569365 - - -"------------------------------------------------------------------------------- -" Test 73: :throw across :function, :delfunction {{{1 -" -" The :function and :delfunction commands may cause an expression -" specified in braces to be evaluated. During evaluation, an -" exception might be thrown. The exception can be caught by the -" script. -"------------------------------------------------------------------------------- - -XpathINIT - -let taken = "" - -function! THROW(x, n) - let g:taken = g:taken . "T" . a:n - throw a:x -endfunction - -function! EXPR(x, n) - let g:taken = g:taken . "E" . a:n - if a:n % 2 == 0 - call THROW(a:x, a:n) - endif - return 2 - a:n % 2 -endfunction - -try - try - " Define function. - Xpath 1 " X: 1 - function! F0() - endfunction - Xpath 2 " X: 2 - function! F{EXPR("function-def-ok", 1)}() - endfunction - Xpath 4 " X: 4 - function! F{EXPR("function-def", 2)}() - endfunction - Xpath 8 " X: 0 - catch /^function-def-ok$/ - Xpath 16 " X: 0 - catch /^function-def$/ - Xpath 32 " X: 32 - catch /.*/ - Xpath 64 " X: 0 - Xout "def:" v:exception "in" v:throwpoint - endtry - - try - " List function. - Xpath 128 " X: 128 - function F0 - Xpath 256 " X: 256 - function F{EXPR("function-lst-ok", 3)} - Xpath 512 " X: 512 - function F{EXPR("function-lst", 4)} - Xpath 1024 " X: 0 - catch /^function-lst-ok$/ - Xpath 2048 " X: 0 - catch /^function-lst$/ - Xpath 4096 " X: 4096 - catch /.*/ - Xpath 8192 " X: 0 - Xout "lst:" v:exception "in" v:throwpoint - endtry - - try - " Delete function - Xpath 16384 " X: 16384 - delfunction F0 - Xpath 32768 " X: 32768 - delfunction F{EXPR("function-del-ok", 5)} - Xpath 65536 " X: 65536 - delfunction F{EXPR("function-del", 6)} - Xpath 131072 " X: 0 - catch /^function-del-ok$/ - Xpath 262144 " X: 0 - catch /^function-del$/ - Xpath 524288 " X: 524288 - catch /.*/ - Xpath 1048576 " X: 0 - Xout "del:" v:exception "in" v:throwpoint - endtry - - let expected = "E1E2T2E3E4T4E5E6T6" - if taken != expected - Xpath 2097152 " X: 0 - Xout "'taken' is" taken "instead of" expected - endif - -catch /.*/ - Xpath 4194304 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 8388608 " X: 8388608 - -unlet taken expected -delfunction THROW -delfunction EXPR - -Xcheck 9032615 - - -"------------------------------------------------------------------------------- -" Test 74: :throw across builtin functions and commands {{{1 -" -" Some functions like exists(), searchpair() take expression -" arguments, other functions or commands like substitute() or -" :substitute cause an expression (specified in the regular -" expression) to be evaluated. During evaluation an exception -" might be thrown. The exception can be caught by the script. -"------------------------------------------------------------------------------- - -XpathINIT - -let taken = "" - -function! THROW(x, n) - let g:taken = g:taken . "T" . a:n - throw a:x -endfunction - -function! EXPR(x, n) - let g:taken = g:taken . "E" . a:n - call THROW(a:x . a:n, a:n) - return "EXPR" -endfunction - -function! SKIP(x, n) - let g:taken = g:taken . "S" . a:n . "(" . line(".") - let theline = getline(".") - if theline =~ "skip" - let g:taken = g:taken . "s)" - return 1 - elseif theline =~ "throw" - let g:taken = g:taken . "t)" - call THROW(a:x . a:n, a:n) - else - let g:taken = g:taken . ")" - return 0 - endif -endfunction - -function! SUBST(x, n) - let g:taken = g:taken . "U" . a:n . "(" . line(".") - let theline = getline(".") - if theline =~ "not" " SUBST() should not be called for this line - let g:taken = g:taken . "n)" - call THROW(a:x . a:n, a:n) - elseif theline =~ "throw" - let g:taken = g:taken . "t)" - call THROW(a:x . a:n, a:n) - else - let g:taken = g:taken . ")" - return "replaced" - endif -endfunction - -try - try - Xpath 1 " X: 1 - let result = exists('*{EXPR("exists", 1)}') - Xpath 2 " X: 0 - catch /^exists1$/ - Xpath 4 " X: 4 - try - let result = exists('{EXPR("exists", 2)}') - Xpath 8 " X: 0 - catch /^exists2$/ - Xpath 16 " X: 16 - catch /.*/ - Xpath 32 " X: 0 - Xout "exists2:" v:exception "in" v:throwpoint - endtry - catch /.*/ - Xpath 64 " X: 0 - Xout "exists1:" v:exception "in" v:throwpoint - endtry - - try - let file = tempname() - exec "edit" file - insert -begin - xx -middle 3 - xx -middle 5 skip - xx -middle 7 throw - xx -end -. - normal! gg - Xpath 128 " X: 128 - let result = - \ searchpair("begin", "middle", "end", '', 'SKIP("searchpair", 3)') - Xpath 256 " X: 256 - let result = - \ searchpair("begin", "middle", "end", '', 'SKIP("searchpair", 4)') - Xpath 512 " X: 0 - let result = - \ searchpair("begin", "middle", "end", '', 'SKIP("searchpair", 5)') - Xpath 1024 " X: 0 - catch /^searchpair[35]$/ - Xpath 2048 " X: 0 - catch /^searchpair4$/ - Xpath 4096 " X: 4096 - catch /.*/ - Xpath 8192 " X: 0 - Xout "searchpair:" v:exception "in" v:throwpoint - finally - bwipeout! - call delete(file) - endtry - - try - let file = tempname() - exec "edit" file - insert -subst 1 -subst 2 -not -subst 4 -subst throw -subst 6 -. - normal! gg - Xpath 16384 " X: 16384 - 1,2substitute/subst/\=SUBST("substitute", 6)/ - try - Xpath 32768 " X: 32768 - try - let v:errmsg = "" - 3substitute/subst/\=SUBST("substitute", 7)/ - finally - if v:errmsg != "" - " If exceptions are not thrown on errors, fake the error - " exception in order to get the same execution path. - throw "faked Vim(substitute)" - endif - endtry - catch /Vim(substitute)/ " Pattern not found ('e' flag missing) - Xpath 65536 " X: 65536 - 3substitute/subst/\=SUBST("substitute", 8)/e - Xpath 131072 " X: 131072 - endtry - Xpath 262144 " X: 262144 - 4,6substitute/subst/\=SUBST("substitute", 9)/ - Xpath 524288 " X: 0 - catch /^substitute[678]/ - Xpath 1048576 " X: 0 - catch /^substitute9/ - Xpath 2097152 " X: 2097152 - finally - bwipeout! - call delete(file) - endtry - - try - Xpath 4194304 " X: 4194304 - let var = substitute("sub", "sub", '\=THROW("substitute()y", 10)', '') - Xpath 8388608 " X: 0 - catch /substitute()y/ - Xpath 16777216 " X: 16777216 - catch /.*/ - Xpath 33554432 " X: 0 - Xout "substitute()y:" v:exception "in" v:throwpoint - endtry - - try - Xpath 67108864 " X: 67108864 - let var = substitute("not", "sub", '\=THROW("substitute()n", 11)', '') - Xpath 134217728 " X: 134217728 - catch /substitute()n/ - Xpath 268435456 " X: 0 - catch /.*/ - Xpath 536870912 " X: 0 - Xout "substitute()n:" v:exception "in" v:throwpoint - endtry - - let expected = "E1T1E2T2S3(3)S4(5s)S4(7t)T4U6(1)U6(2)U9(4)U9(5t)T9T10" - if taken != expected - Xpath 1073741824 " X: 0 - Xout "'taken' is" taken "instead of" expected - endif - -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -unlet result var taken expected -delfunction THROW -delfunction EXPR -delfunction SKIP -delfunction SUBST - -Xcheck 224907669 - - -"------------------------------------------------------------------------------- -" Test 75: Errors in builtin functions. {{{1 -" -" On an error in a builtin function called inside a :try/:endtry -" region, the evaluation of the expression calling that function and -" the command containing that expression are abandoned. The error can -" be caught as an exception. -" -" A simple :call of the builtin function is a trivial case. If the -" builtin function is called in the argument list of another function, -" no further arguments are evaluated, and the other function is not -" executed. If the builtin function is called from the argument of -" a :return command, the :return command is not executed. If the -" builtin function is called from the argument of a :throw command, -" the :throw command is not executed. The evaluation of the -" expression calling the builtin function is abandoned. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F1(arg1) - Xpath 1 " X: 0 -endfunction - -function! F2(arg1, arg2) - Xpath 2 " X: 0 -endfunction - -function! G() - Xpath 4 " X: 0 -endfunction - -function! H() - Xpath 8 " X: 0 -endfunction - -function! R() - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 16 " X: 16 - return append(1, "s") - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 32 " X: 0 - finally - Xpath 64 " X: 64 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 128 " X: 128 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - Xpath 256 " X: 256 -endfunction - -try - set noma " let append() fail with "E21" - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 512 " X: 512 - call append(1, "s") - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 4096 " X: 4096 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 8192 " X: 8192 - call F1('x' . append(1, "s")) - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 16384 " X: 0 - finally - Xpath 32768 " X: 32768 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 65536 " X: 65536 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 131072 " X: 131072 - call F2('x' . append(1, "s"), G()) - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 262144 " X: 0 - finally - Xpath 524288 " X: 524288 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 1048576 " X: 1048576 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - call R() - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 2097152 " X: 2097152 - throw "T" . append(1, "s") - catch /E21/ - let caught = 1 - catch /^T.*/ - Xpath 4194304 " X: 0 - catch /.*/ - Xpath 8388608 " X: 0 - finally - Xpath 16777216 " X: 16777216 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 33554432 " X: 33554432 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 67108864 " X: 67108864 - let x = "a" - let x = x . "b" . append(1, "s") . H() - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 134217728 " X: 0 - finally - Xpath 268435456 " X: 268435456 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 536870912 " X: 536870912 - endif - if x == "a" - Xpath 1073741824 " X: 1073741824 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -finally - set ma& -endtry - -unlet! caught x -delfunction F1 -delfunction F2 -delfunction G -delfunction H -delfunction R - -Xcheck 2000403408 +" Tests 69 to 75 were moved to test_trycatch.vim +let Xtest = 76 "------------------------------------------------------------------------------- @@ -8780,36 +6523,9 @@ delfunction F Xout "No Crash for vimgrep on BufUnload" Xcheck 0 -"------------------------------------------------------------------------------- -" Test 87 using (expr) ? funcref : funcref {{{1 -" -" Vim needs to correctly parse the funcref and even when it does -" not execute the funcref, it needs to consume the trailing () -"------------------------------------------------------------------------------- - -XpathINIT - -func Add2(x1, x2) - return a:x1 + a:x2 -endfu - -func GetStr() - return "abcdefghijklmnopqrstuvwxyp" -endfu +" Test 87 was moved to test_vimscript.vim +let Xtest = 88 -echo function('Add2')(2,3) - -Xout 1 ? function('Add2')(1,2) : function('Add2')(2,3) -Xout 0 ? function('Add2')(1,2) : function('Add2')(2,3) -" Make sure, GetStr() still works. -Xout GetStr()[0:10] - - -delfunction GetStr -delfunction Add2 -Xout "Successfully executed funcref Add2" - -Xcheck 0 "------------------------------------------------------------------------------- " Test 88: $VIMNOERRTHROW and $VIMNOINTTHROW support {{{1 diff --git a/src/nvim/testdir/test_backspace_opt.vim b/src/nvim/testdir/test_backspace_opt.vim new file mode 100644 index 0000000000..d680b442db --- /dev/null +++ b/src/nvim/testdir/test_backspace_opt.vim @@ -0,0 +1,151 @@ +" Tests for 'backspace' settings + +func Exec(expr) + let str='' + try + exec a:expr + catch /.*/ + let str=v:exception + endtry + return str +endfunc + +func Test_backspace_option() + set backspace= + call assert_equal('', &backspace) + set backspace=indent + call assert_equal('indent', &backspace) + set backspace=eol + call assert_equal('eol', &backspace) + set backspace=start + call assert_equal('start', &backspace) + set backspace=nostop + call assert_equal('nostop', &backspace) + " Add the value + set backspace= + set backspace=indent + call assert_equal('indent', &backspace) + set backspace+=eol + call assert_equal('indent,eol', &backspace) + set backspace+=start + call assert_equal('indent,eol,start', &backspace) + set backspace+=nostop + call assert_equal('indent,eol,start,nostop', &backspace) + " Delete the value + set backspace-=nostop + call assert_equal('indent,eol,start', &backspace) + set backspace-=indent + call assert_equal('eol,start', &backspace) + set backspace-=start + call assert_equal('eol', &backspace) + set backspace-=eol + call assert_equal('', &backspace) + " Check the error + call assert_equal(0, match(Exec('set backspace=ABC'), '.*E474')) + call assert_equal(0, match(Exec('set backspace+=def'), '.*E474')) + " NOTE: Vim doesn't check following error... + "call assert_equal(0, match(Exec('set backspace-=ghi'), '.*E474')) + + " Check backwards compatibility with version 5.4 and earlier + set backspace=0 + call assert_equal('0', &backspace) + set backspace=1 + call assert_equal('1', &backspace) + set backspace=2 + call assert_equal('2', &backspace) + set backspace=3 + call assert_equal('3', &backspace) + call assert_false(match(Exec('set backspace=4'), '.*E474')) + call assert_false(match(Exec('set backspace=10'), '.*E474')) + + " Cleared when 'compatible' is set + " set compatible + " call assert_equal('', &backspace) + set nocompatible viminfo+=nviminfo +endfunc + +" Test with backspace set to the non-compatible setting +func Test_backspace_ctrl_u() + new + call append(0, [ + \ "1 this shouldn't be deleted", + \ "2 this shouldn't be deleted", + \ "3 this shouldn't be deleted", + \ "4 this should be deleted", + \ "5 this shouldn't be deleted", + \ "6 this shouldn't be deleted", + \ "7 this shouldn't be deleted", + \ "8 this shouldn't be deleted (not touched yet)"]) + call cursor(2, 1) + + " set compatible + set backspace=2 + + exe "normal Avim1\<C-U>\<Esc>\<CR>" + exe "normal Avim2\<C-G>u\<C-U>\<Esc>\<CR>" + + set cpo-=< + inoremap <c-u> <left><c-u> + exe "normal Avim3\<C-U>\<Esc>\<CR>" + iunmap <c-u> + exe "normal Avim4\<C-U>\<C-U>\<Esc>\<CR>" + + " Test with backspace set to the compatible setting + set backspace= visualbell + exe "normal A vim5\<Esc>A\<C-U>\<C-U>\<Esc>\<CR>" + exe "normal A vim6\<Esc>Azwei\<C-G>u\<C-U>\<Esc>\<CR>" + + inoremap <c-u> <left><c-u> + exe "normal A vim7\<C-U>\<C-U>\<Esc>\<CR>" + + call assert_equal([ + \ "1 this shouldn't be deleted", + \ "2 this shouldn't be deleted", + \ "3 this shouldn't be deleted", + \ "4 this should be deleted3", + \ "", + \ "6 this shouldn't be deleted vim5", + \ "7 this shouldn't be deleted vim6", + \ "8 this shouldn't be deleted (not touched yet) vim7", + \ ""], getline(1, '$')) + + " Reset values + set compatible&vim + set visualbell&vim + set backspace&vim + + " Test new nostop option + %d_ + let expected = "foo bar foobar" + call setline(1, expected) + call cursor(1, 8) + exe ":norm! ianotherone\<c-u>" + call assert_equal(expected, getline(1)) + call cursor(1, 8) + exe ":norm! ianothertwo\<c-w>" + call assert_equal(expected, getline(1)) + + let content = getline(1) + for value in ['indent,nostop', 'eol,nostop', 'indent,eol,nostop', 'indent,eol,start,nostop'] + exe ":set bs=".. value + %d _ + call setline(1, content) + let expected = " foobar" + call cursor(1, 8) + exe ":norm! ianotherone\<c-u>" + call assert_equal(expected, getline(1), 'CTRL-U backspace value: '.. &bs) + let expected = "foo foobar" + call setline(1, content) + call cursor(1, 8) + exe ":norm! ianothertwo\<c-w>" + call assert_equal(expected, getline(1), 'CTRL-W backspace value: '.. &bs) + endfor + + " Reset options + set compatible&vim + set visualbell&vim + set backspace&vim + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab 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_const.vim b/src/nvim/testdir/test_const.vim index fc7ea71f6e..ea69c8cba4 100644 --- a/src/nvim/testdir/test_const.vim +++ b/src/nvim/testdir/test_const.vim @@ -109,7 +109,7 @@ func Test_define_script_var_with_lock() unlet s:x endfunc -func Test_descructuring_with_lock() +func Test_destructuring_with_lock() const [a, b, c] = [1, 1.1, 'vim'] call assert_fails('let a = 1', 'E741:') 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_excmd.vim b/src/nvim/testdir/test_excmd.vim index 4a027c3864..20508b12d3 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -1,5 +1,7 @@ " Tests for various Ex commands. +source check.vim + func Test_ex_delete() new call setline(1, ['a', 'b', 'c']) @@ -9,6 +11,17 @@ func Test_ex_delete() call assert_equal(['a', 'c'], getline(1, 2)) endfunc +func Test_range_error() + call assert_fails(':.echo 1', 'E481:') + call assert_fails(':$echo 1', 'E481:') + call assert_fails(':1,2echo 1', 'E481:') + call assert_fails(':+1echo 1', 'E481:') + call assert_fails(':/1/echo 1', 'E481:') + call assert_fails(':\/echo 1', 'E481:') + normal vv + call assert_fails(":'<,'>echo 1", 'E481:') +endfunc + func Test_buffers_lastused() edit bufc " oldest @@ -40,3 +53,81 @@ func Test_buffers_lastused() bwipeout bufb bwipeout bufc endfunc + +" Test for the :confirm command dialog +func Test_confirm_cmd() + CheckNotGui + CheckRunVimInTerminal + call writefile(['foo1'], 'foo') + call writefile(['bar1'], 'bar') + " Test for saving all the modified buffers + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":new foo\n") + call term_sendkeys(buf, ":call setline(1, 'foo2')\n") + call term_sendkeys(buf, ":new bar\n") + call term_sendkeys(buf, ":call setline(1, 'bar2')\n") + call term_sendkeys(buf, ":wincmd b\n") + call term_sendkeys(buf, ":confirm qall\n") + call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "A") + call StopVimInTerminal(buf) + call assert_equal(['foo2'], readfile('foo')) + call assert_equal(['bar2'], readfile('bar')) + " Test for discarding all the changes to modified buffers + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":new foo\n") + call term_sendkeys(buf, ":call setline(1, 'foo3')\n") + call term_sendkeys(buf, ":new bar\n") + call term_sendkeys(buf, ":call setline(1, 'bar3')\n") + call term_sendkeys(buf, ":wincmd b\n") + call term_sendkeys(buf, ":confirm qall\n") + call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "D") + call StopVimInTerminal(buf) + call assert_equal(['foo2'], readfile('foo')) + call assert_equal(['bar2'], readfile('bar')) + " Test for saving and discarding changes to some buffers + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":new foo\n") + call term_sendkeys(buf, ":call setline(1, 'foo4')\n") + call term_sendkeys(buf, ":new bar\n") + call term_sendkeys(buf, ":call setline(1, 'bar4')\n") + call term_sendkeys(buf, ":wincmd b\n") + call term_sendkeys(buf, ":confirm qall\n") + call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "N") + call WaitForAssert({-> assert_match('\[Y\]es, (N)o, (C)ancel: ', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "Y") + call StopVimInTerminal(buf) + call assert_equal(['foo4'], readfile('foo')) + call assert_equal(['bar2'], readfile('bar')) + + call delete('foo') + call delete('bar') +endfunc + +func Test_confirm_cmd_cancel() + CheckNotGui + CheckRunVimInTerminal + + " Test for closing a window with a modified buffer + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":new\n") + call term_sendkeys(buf, ":call setline(1, 'abc')\n") + call term_sendkeys(buf, ":confirm close\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "C") + call WaitForAssert({-> assert_equal('', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, ":confirm close\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "N") + call WaitForAssert({-> assert_match('^ *0,0-1 All$', + \ term_getline(buf, 20))}, 1000) + call StopVimInTerminal(buf) +endfunc diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim index 8dc25f62b1..465613f1cf 100644 --- a/src/nvim/testdir/test_fileformat.vim +++ b/src/nvim/testdir/test_fileformat.vim @@ -31,3 +31,26 @@ func Test_fileformat_autocommand() au! BufReadPre Xfile bw! endfunc + +" Test for changing the fileformat using ++read +func Test_fileformat_plusplus_read() + new + call setline(1, ['one', 'two', 'three']) + w ++ff=dos Xfile1 + enew! + set ff=unix + " A :read doesn't change the fileformat, but does apply to the read lines. + r ++fileformat=unix Xfile1 + call assert_equal('unix', &fileformat) + call assert_equal("three\r", getline('$')) + 3r ++edit Xfile1 + call assert_equal('dos', &fileformat) + close! + call delete('Xfile1') + set fileformat& + call assert_fails('e ++fileformat Xfile1', 'E474:') + call assert_fails('e ++ff=abc Xfile1', 'E474:') + call assert_fails('e ++abc1 Xfile1', 'E474:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 52b5884c8b..1cb68e7fef 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -150,6 +150,7 @@ let s:filename_checks = { \ 'dsl': ['file.dsl'], \ 'dtd': ['file.dtd'], \ 'dts': ['file.dts', 'file.dtsi'], + \ 'dune': ['jbuild', 'dune', 'dune-project', 'dune-workspace'], \ 'dylan': ['file.dylan'], \ 'dylanintr': ['file.intr'], \ 'dylanlid': ['file.lid'], @@ -181,6 +182,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'], @@ -321,9 +323,10 @@ let s:filename_checks = { \ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom'], \ 'nsis': ['file.nsi', 'file.nsh'], \ 'obj': ['file.obj'], - \ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit'], + \ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit', 'file.mlt', 'file.mlp', 'file.mlip', 'file.mli.cppo', 'file.ml.cppo'], \ 'occam': ['file.occ'], \ 'omnimark': ['file.xom', 'file.xin'], + \ 'opam': ['opam', 'file.opam', 'file.opam.template'], \ 'openroad': ['file.or'], \ 'ora': ['file.ora'], \ 'pamconf': ['/etc/pam.conf'], @@ -397,6 +400,7 @@ let s:filename_checks = { \ 'scheme': ['file.scm', 'file.ss', 'file.rkt'], \ 'scilab': ['file.sci', 'file.sce'], \ 'screen': ['.screenrc', 'screenrc'], + \ 'sexplib': ['file.sexp'], \ 'scss': ['file.scss'], \ 'sd': ['file.sd'], \ 'sdc': ['file.sdc'], @@ -463,6 +467,7 @@ let s:filename_checks = { \ 'tilde': ['file.t.html'], \ 'tli': ['file.tli'], \ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf'], + \ 'toml': ['file.toml'], \ 'tpp': ['file.tpp'], \ 'treetop': ['file.treetop'], \ 'trustees': ['trustees.conf'], @@ -471,7 +476,6 @@ let s:filename_checks = { \ 'tssgm': ['file.tssgm'], \ 'tssop': ['file.tssop'], \ 'twig': ['file.twig'], - \ 'typescript': ['file.ts'], \ 'typescriptreact': ['file.tsx'], \ 'uc': ['file.uc'], \ 'udevconf': ['/etc/udev/udev.conf'], @@ -668,5 +672,22 @@ func Test_hook_file() filetype off endfunc +func Test_ts_file() + filetype on + + call writefile(['<?xml version="1.0" encoding="utf-8"?>'], 'Xfile.ts') + split Xfile.ts + call assert_equal('xml', &filetype) + bwipe! + + call writefile(['// looks like Typescript'], 'Xfile.ts') + split Xfile.ts + call assert_equal('typescript', &filetype) + bwipe! + + call delete('Xfile.hook') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_find_complete.vim b/src/nvim/testdir/test_find_complete.vim index a7bc135d47..0a00d9432f 100644 --- a/src/nvim/testdir/test_find_complete.vim +++ b/src/nvim/testdir/test_find_complete.vim @@ -15,22 +15,22 @@ func Test_find_complete() new set path= - call assert_fails('call feedkeys(":find\t\n", "xt")', 'E345:') + call assert_fails('call feedkeys(":find \t\n", "xt")', 'E471:') close new set path=. - call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + call assert_fails('call feedkeys(":find \t\n", "xt")', 'E471:') close new set path=.,, - call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + call assert_fails('call feedkeys(":find \t\n", "xt")', 'E471:') close new set path=./** - call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + call assert_fails('call feedkeys(":find \t\n", "xt")', 'E471:') close " We shouldn't find any file till this point diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim index f5488a6a27..2195bf527e 100644 --- a/src/nvim/testdir/test_findfile.vim +++ b/src/nvim/testdir/test_findfile.vim @@ -184,3 +184,46 @@ func Test_finddir_error() call assert_fails('call finddir("x", "**x")', 'E343:') call assert_fails('call finddir("x", repeat("x", 5000))', 'E854:') endfunc + +" Test for the :find, :sfind and :tabfind commands +func Test_find_cmd() + new + let save_path = &path + let save_dir = getcwd() + set path=.,./**/* + call CreateFiles() + cd Xdir1 + " Test for :find + find foo + call assert_equal('foo', expand('%:.')) + 2find foo + call assert_equal('Xdir2/foo', expand('%:.')) + call assert_fails('3find foo', 'E347:') + " Test for :sfind + enew + sfind barfoo + call assert_equal('Xdir2/Xdir3/barfoo', expand('%:.')) + call assert_equal(3, winnr('$')) + close + call assert_fails('sfind baz', 'E345:') + call assert_equal(2, winnr('$')) + " Test for :tabfind + enew + tabfind foobar + call assert_equal('Xdir2/foobar', expand('%:.')) + call assert_equal(2, tabpagenr('$')) + tabclose + call assert_fails('tabfind baz', 'E345:') + call assert_equal(1, tabpagenr('$')) + " call chdir(save_dir) + exe 'cd ' . save_dir + call CleanFiles() + let &path = save_path + close + + call assert_fails('find', 'E471:') + call assert_fails('sfind', 'E471:') + call assert_fails('tabfind', 'E471:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 3c90c45952..88ce64b9eb 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -815,4 +815,11 @@ func Test_fold_create_delete_create() bwipe! endfunc +" this was crashing +func Test_fold_create_delete() + new + norm zFzFzdzj + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab 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_hardcopy.vim b/src/nvim/testdir/test_hardcopy.vim index 6125f9b993..e390bd5cc8 100644 --- a/src/nvim/testdir/test_hardcopy.vim +++ b/src/nvim/testdir/test_hardcopy.vim @@ -1,5 +1,7 @@ " Test :hardcopy +source check.vim + func Test_printoptions() edit test_hardcopy.vim syn on @@ -8,8 +10,10 @@ func Test_printoptions() \ 'left:2in,top:30pt,right:16mm,bottom:3pc', \ 'header:3,syntax:y,number:y,wrap:n', \ 'header:3,syntax:n,number:y,wrap:y', + \ 'header:0,syntax:a,number:y,wrap:y', \ 'duplex:short,collate:n,jobsplit:y,portrait:n', \ 'duplex:long,collate:y,jobsplit:n,portrait:y', + \ 'duplex:off,collate:y,jobsplit:y,portrait:y', \ 'paper:10x14', \ 'paper:A3', \ 'paper:A4', @@ -28,7 +32,7 @@ func Test_printoptions() \ ''] exe 'set printoptions=' .. opt if has('postscript') - hardcopy > Xhardcopy_printoptions + 1,50hardcopy > Xhardcopy_printoptions let lines = readfile('Xhardcopy_printoptions') call assert_true(len(lines) > 20, opt) call assert_true(lines[0] =~ 'PS-Adobe', opt) @@ -44,8 +48,8 @@ func Test_printoptions() endfunc func Test_printmbfont() - " Print a small help page which contains tabs to cover code that expands tabs to spaces. - help help + " Print a help page which contains tabs, underlines (etc) to recover more code. + help syntax.txt syn on for opt in [':WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-Bold-Italic,c:yes,a:no', @@ -63,10 +67,39 @@ func Test_printmbfont() bwipe endfunc +func Test_printmbcharset() + CheckFeature postscript + + " digraph.txt has plenty of non-latin1 characters. + help digraph.txt + set printmbcharset=ISO10646 printencoding=utf-8 + for courier in ['yes', 'no'] + for ascii in ['yes', 'no'] + exe 'set printmbfont=r:WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-BoldItalic' + \ .. ',c:' .. courier .. ',a:' .. ascii + hardcopy > Xhardcopy_printmbcharset + let lines = readfile('Xhardcopy_printmbcharset') + call assert_true(len(lines) > 20) + call assert_true(lines[0] =~ 'PS-Adobe') + endfor + endfor + + set printmbcharset=does-not-exist printencoding=utf-8 printmbfont=r:WadaMin-Regular + call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E456:') + + set printmbcharset=GB_2312-80 printencoding=utf-8 printmbfont=r:WadaMin-Regular + call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E673:') + + set printmbcharset=ISO10646 printencoding=utf-8 printmbfont= + call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E675:') + + call delete('Xhardcopy_printmbcharset') + set printmbcharset& printencoding& printmbfont& + bwipe +endfunc + func Test_printexpr() - if !has('unix') - return - endif + CheckFeature postscript " Not a very useful printexpr value, but enough to test " hardcopy with 'printexpr'. @@ -84,7 +117,7 @@ func Test_printexpr() \ readfile('Xhardcopy_printexpr')) call delete('Xhardcopy_printexpr') - " Function return 1 to test print failure. + " Function returns 1 to test print failure. function PrintFails(fname) call delete(a:fname) return 1 @@ -97,12 +130,11 @@ func Test_printexpr() endfunc func Test_errors() - " FIXME: Windows fails differently than Unix. - if has('unix') - edit test_hardcopy.vim - call assert_fails('hardcopy >', 'E324:') - bwipe - endif + CheckFeature postscript + + edit test_hardcopy.vim + call assert_fails('hardcopy >', 'E324:') + bwipe endfunc func Test_dark_background() @@ -126,12 +158,11 @@ func Test_dark_background() endfun func Test_empty_buffer() - " FIXME: Unclear why this fails on Windows. - if has('unix') - new - call assert_equal("\nNo text to be printed", execute('hardcopy')) - bwipe - endif + CheckFeature postscript + + new + call assert_equal("\nNo text to be printed", execute('hardcopy')) + bwipe endfunc func Test_printheader_parsing() @@ -145,9 +176,8 @@ func Test_printheader_parsing() endfunc func Test_fname_with_spaces() - if !has('postscript') - return - endif + CheckFeature postscript + split t\ e\ s\ t.txt call setline(1, ['just', 'some', 'text']) hardcopy > %.ps @@ -157,9 +187,11 @@ func Test_fname_with_spaces() endfunc func Test_illegal_byte() - if !has('postscript') || &enc != 'utf-8' + CheckFeature postscript + if &enc != 'utf-8' return endif + new " conversion of 0xff will fail, this used to cause a crash call setline(1, "\xff") @@ -168,3 +200,5 @@ func Test_illegal_byte() bwipe! call delete('Xpstest') endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index 01fb9917e9..8e59efd22d 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -56,3 +56,49 @@ func Test_help_local_additions() call delete('Xruntime', 'rf') let &rtp = rtp_save endfunc + +" Test for the :helptags command +func Test_helptag_cmd() + call mkdir('Xdir/a/doc', 'p') + + " No help file to process in the directory + call assert_fails('helptags Xdir', 'E151:') + + call writefile([], 'Xdir/a/doc/sample.txt') + + " Test for ++t argument + helptags ++t Xdir + call assert_equal(["help-tags\ttags\t1"], readfile('Xdir/tags')) + call delete('Xdir/tags') + + " The following tests fail on FreeBSD for some reason + if has('unix') && !has('bsd') + " Read-only tags file + call mkdir('Xdir/doc', 'p') + call writefile([''], 'Xdir/doc/tags') + call writefile([], 'Xdir/doc/sample.txt') + call setfperm('Xdir/doc/tags', 'r-xr--r--') + call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags')) + + let rtp = &rtp + let &rtp = 'Xdir' + helptags ALL + let &rtp = rtp + + call delete('Xdir/doc/tags') + + " No permission to read the help file + call setfperm('Xdir/a/doc/sample.txt', '-w-------') + call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/a/doc/sample.txt')) + call delete('Xdir/a/doc/sample.txt') + call delete('Xdir/tags') + endif + + " Duplicate tags in the help file + call writefile(['*tag1*', '*tag1*', '*tag2*'], 'Xdir/a/doc/sample.txt') + call assert_fails('helptags Xdir', 'E154:') + + call delete('Xdir', 'rf') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 57a0a7aaf4..9435931d41 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -280,7 +280,7 @@ func Test_omni_dash() set omnifunc=Omni new exe "normal Gofind -\<C-x>\<C-o>" - call assert_equal("\n-\nmatch 1 of 2", execute(':2mess')) + call assert_equal("find -help", getline('$')) bwipe! delfunc Omni @@ -312,6 +312,24 @@ func Test_completefunc_args() delfunc CompleteFunc endfunc +func CompleteTest(findstart, query) + if a:findstart + return col('.') + endif + return ['matched'] +endfunc + +func Test_completefunc_info() + new + set completeopt=menuone + set completefunc=CompleteTest + call feedkeys("i\<C-X>\<C-U>\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx") + call assert_equal("matched{'pum_visible': 1, 'mode': 'function', 'selected': 0, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1)) + bwipe! + set completeopt& + set completefunc& +endfunc + " Check that when using feedkeys() typeahead does not interrupt searching for " completions. func Test_compl_feedkeys() @@ -450,3 +468,17 @@ func Test_pum_with_folds_two_tabs() call StopVimInTerminal(buf) call delete('Xpumscript') endfunc + +" Test to ensure 'Scanning...' messages are not recorded in messages history +func Test_z1_complete_no_history() + new + messages clear + let currmess = execute('messages') + setlocal dictionary=README.txt + exe "normal owh\<C-X>\<C-K>" + exe "normal owh\<C-N>" + call assert_equal(currmess, execute('messages')) + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 66df57ea39..2fd82a4b6d 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -171,6 +171,11 @@ func Test_delmarks() " Deleting an already deleted mark should not fail. delmarks x + " getpos() should return all zeros after deleting a filemark. + norm mA + delmarks A + call assert_equal([0, 0, 0, 0], getpos("'A")) + " Test deleting a range of marks. norm ma norm mb @@ -201,3 +206,28 @@ func Test_mark_error() call assert_fails('mark xx', 'E488:') call assert_fails('mark _', 'E191:') endfunc + +" Test for the getmarklist() function +func Test_getmarklist() + new + " global marks + delmarks A-Z 0-9 \" ^.[] + call assert_equal([], getmarklist()) + call setline(1, ['one', 'two', 'three']) + mark A + call cursor(3, 5) + normal mN + call assert_equal([{'file' : '', 'mark' : "'A", 'pos' : [bufnr(), 1, 1, 0]}, + \ {'file' : '', 'mark' : "'N", 'pos' : [bufnr(), 3, 5, 0]}], + \ getmarklist()) + " buffer local marks + delmarks! + call assert_equal([{'mark' : "''", 'pos' : [bufnr(), 1, 1, 0]}, + \ {'mark' : "'\"", 'pos' : [bufnr(), 1, 1, 0]}], getmarklist(bufnr())) + call cursor(2, 2) + normal mr + call assert_equal({'mark' : "'r", 'pos' : [bufnr(), 2, 2, 0]}, + \ getmarklist(bufnr())[0]) + call assert_equal([], getmarklist({})) + close! +endfunc diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 215065f941..f71da92bf8 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -128,6 +128,7 @@ func Test_mksession() call delete('Xtest_mks.out') call delete(tmpfile) let &wrap = wrap_save + set sessionoptions& endfunc func Test_mksession_winheight() @@ -154,7 +155,7 @@ func Test_mksession_rtp() return endif new - set sessionoptions+=options + set sessionoptions&vi let _rtp=&rtp " Make a real long (invalid) runtimepath value, " that should exceed PATH_MAX (hopefully) @@ -174,17 +175,23 @@ func Test_mksession_rtp() call assert_equal(expected, li) call delete('Xtest_mks.out') + 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 @@ -218,6 +225,72 @@ 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() + set sessionoptions&vi + set hlsearch + mksession! Xtest_mks.out + nohlsearch + source Xtest_mks.out + call assert_equal(1, v:hlsearch, 'session should restore search highlighting state') + nohlsearch + mksession! Xtest_mks.out + source Xtest_mks.out + call assert_equal(0, v:hlsearch, 'session should restore search highlighting state') + set sessionoptions& + call delete('Xtest_mks.out') +endfunc + +endif + " Test :mkview with a file argument. func Test_mkview_file() " Create a view with line number and a fold. @@ -335,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 @@ -411,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_normal.vim b/src/nvim/testdir/test_normal.vim index ad6d325510..7a846e5ea0 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -222,6 +222,21 @@ func Test_normal05_formatexpr_setopt() set formatexpr= endfunc +" When 'formatexpr' returns non-zero, internal formatting is used. +func Test_normal_formatexpr_returns_nonzero() + new + call setline(1, ['one', 'two']) + func! Format() + return 1 + endfunc + setlocal formatexpr=Format() + normal VGgq + call assert_equal(['one two'], getline(1, '$')) + setlocal formatexpr= + delfunc Format + close! +endfunc + func Test_normal06_formatprg() " basic test for formatprg " only test on non windows platform diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 15c1836c9a..ccc5e6ffc9 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -348,6 +348,16 @@ func Test_set_values() endif endfunc +func Test_renderoptions() + throw 'skipped: Nvim does not support renderoptions' + " Only do this for Windows Vista and later, fails on Windows XP and earlier. + " Doesn't hurt to do this on a non-Windows system. + if windowsversion() !~ '^[345]\.' + set renderoptions=type:directx + set rop=type:directx + endif +endfunc + func ResetIndentexpr() set indentexpr= endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index e06c4f59da..48c0a83053 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -29,7 +29,7 @@ func s:setup_commands(cchar) command! -count -nargs=* -bang Xprev <mods><count>cprev<bang> <args> command! -nargs=* -bang Xfirst <mods>cfirst<bang> <args> command! -nargs=* -bang Xlast <mods>clast<bang> <args> - command! -nargs=* -bang -range Xnfile <mods><count>cnfile<bang> <args> + command! -count -nargs=* -bang Xnfile <mods><count>cnfile<bang> <args> command! -nargs=* -bang Xpfile <mods>cpfile<bang> <args> command! -nargs=* Xexpr <mods>cexpr <args> command! -range -nargs=* Xvimgrep <mods><count>vimgrep <args> @@ -40,6 +40,8 @@ func s:setup_commands(cchar) command! -nargs=0 -count Xcc <count>cc command! -count=1 -nargs=0 Xbelow <mods><count>cbelow command! -count=1 -nargs=0 Xabove <mods><count>cabove + command! -count=1 -nargs=0 Xbefore <mods><count>cbefore + command! -count=1 -nargs=0 Xafter <mods><count>cafter let g:Xgetlist = function('getqflist') let g:Xsetlist = function('setqflist') call setqflist([], 'f') @@ -64,7 +66,7 @@ func s:setup_commands(cchar) command! -count -nargs=* -bang Xprev <mods><count>lprev<bang> <args> command! -nargs=* -bang Xfirst <mods>lfirst<bang> <args> command! -nargs=* -bang Xlast <mods>llast<bang> <args> - command! -nargs=* -bang -range Xnfile <mods><count>lnfile<bang> <args> + command! -count -nargs=* -bang Xnfile <mods><count>lnfile<bang> <args> command! -nargs=* -bang Xpfile <mods>lpfile<bang> <args> command! -nargs=* Xexpr <mods>lexpr <args> command! -range -nargs=* Xvimgrep <mods><count>lvimgrep <args> @@ -75,6 +77,8 @@ func s:setup_commands(cchar) command! -nargs=0 -count Xcc <count>ll command! -count=1 -nargs=0 Xbelow <mods><count>lbelow command! -count=1 -nargs=0 Xabove <mods><count>labove + command! -count=1 -nargs=0 Xbefore <mods><count>lbefore + command! -count=1 -nargs=0 Xafter <mods><count>lafter let g:Xgetlist = function('getloclist', [0]) let g:Xsetlist = function('setloclist', [0]) call setloclist(0, [], 'f') @@ -260,6 +264,9 @@ func XwindowTests(cchar) \ winheight('.') == 7 && \ getline('.') ==# '|| non-error 1') + " :cnext in quickfix window should move to the next entry + Xnext + call assert_equal(2, g:Xgetlist({'idx' : 0}).idx) " Calling cwindow should close the quickfix window with no valid errors Xwindow @@ -431,13 +438,19 @@ func Xtest_browse(cchar) " result in failure if a:cchar == 'c' let err = 'E42:' + let cmd = '$cc' else let err = 'E776:' + let cmd = '$ll' endif call assert_fails('Xnext', err) call assert_fails('Xprev', err) call assert_fails('Xnfile', err) call assert_fails('Xpfile', err) + call assert_fails(cmd, err) + + Xexpr '' + call assert_fails(cmd, 'E42:') call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile2') @@ -1247,6 +1260,36 @@ func Test_efm2() let &efm = save_efm endfunc +" Test for '%t' (error type) field in 'efm' +func Test_efm_error_type() + let save_efm = &efm + + " error type + set efm=%f:%l:%t:%m + cexpr ["Xfile1:10:E:msg1", "Xfile1:20:W:msg2", "Xfile1:30:I:msg3", + \ "Xfile1:40:N:msg4", "Xfile1:50:R:msg5"] + let output = split(execute('clist'), "\n") + call assert_equal([ + \ ' 1 Xfile1:10 error: msg1', + \ ' 2 Xfile1:20 warning: msg2', + \ ' 3 Xfile1:30 info: msg3', + \ ' 4 Xfile1:40 note: msg4', + \ ' 5 Xfile1:50 R: msg5'], output) + + " error type and a error number + set efm=%f:%l:%t:%n:%m + cexpr ["Xfile1:10:E:2:msg1", "Xfile1:20:W:4:msg2", "Xfile1:30:I:6:msg3", + \ "Xfile1:40:N:8:msg4", "Xfile1:50:R:3:msg5"] + let output = split(execute('clist'), "\n") + call assert_equal([ + \ ' 1 Xfile1:10 error 2: msg1', + \ ' 2 Xfile1:20 warning 4: msg2', + \ ' 3 Xfile1:30 info 6: msg3', + \ ' 4 Xfile1:40 note 8: msg4', + \ ' 5 Xfile1:50 R 3: msg5'], output) + let &efm = save_efm +endfunc + func XquickfixChangedByAutocmd(cchar) call s:setup_commands(a:cchar) if a:cchar == 'c' @@ -1744,7 +1787,7 @@ func Test_switchbuf() call assert_equal(1, bufwinnr('Xqftestfile3')) " If only quickfix window is open in the current tabpage, jumping to an - " entry with 'switchubf' set to 'usetab' should search in other tabpages. + " entry with 'switchbuf' set to 'usetab' should search in other tabpages. enew | only set switchbuf=usetab tabedit Xqftestfile1 @@ -1810,14 +1853,27 @@ func s:test_xgrep(cchar) enew! | only set makeef&vim silent Xgrep Grep_Test_Text: test_quickfix.vim - call assert_true(len(g:Xgetlist()) == 3) + call assert_true(len(g:Xgetlist()) == 5) Xopen call assert_true(w:quickfix_title =~ '^:grep') Xclose enew set makeef=Temp_File_## silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim - call assert_true(len(g:Xgetlist()) == 6) + + " Try with 'grepprg' set to 'internal' + set grepprg=internal + silent Xgrep Grep_Test_Text: test_quickfix.vim + silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim + call assert_true(len(g:Xgetlist()) == 9) + set grepprg&vim + + call writefile(['Vim'], 'XtestTempFile') + set makeef=XtestTempFile + silent Xgrep Grep_Test_Text: test_quickfix.vim + call assert_equal(5, len(g:Xgetlist())) + call assert_false(filereadable('XtestTempFile')) + set makeef&vim endfunc func Test_grep() @@ -1914,9 +1970,23 @@ func HistoryTest(cchar) call assert_equal(' error list 2 of 3; 2 ' . common, res[1]) call assert_equal('> error list 3 of 3; 3 ' . common, res[2]) + " Test for changing the quickfix lists + call assert_equal(3, g:Xgetlist({'nr' : 0}).nr) + exe '1' . a:cchar . 'hist' + call assert_equal(1, g:Xgetlist({'nr' : 0}).nr) + exe '3' . a:cchar . 'hist' + call assert_equal(3, g:Xgetlist({'nr' : 0}).nr) + call assert_fails('-2' . a:cchar . 'hist', 'E16:') + call assert_fails('4' . a:cchar . 'hist', 'E16:') + call g:Xsetlist([], 'f') let l = split(execute(a:cchar . 'hist'), "\n") call assert_equal('No entries', l[0]) + if a:cchar == 'c' + call assert_fails('4chist', 'E16:') + else + call assert_fails('4lhist', 'E776:') + endif " An empty list should still show the stack history call g:Xsetlist([]) @@ -2365,14 +2435,28 @@ func Test_Autocmd() silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim silent grep abc123def Xtest silent grepadd abc123def Xtest + set grepprg=internal + silent grep Grep_Autocmd_Text test_quickfix.vim + silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim + silent lgrep Grep_Autocmd_Text test_quickfix.vim + silent lgrepadd GrepAdd_Autocmd_Text test_quickfix.vim + set grepprg&vim let l = ['pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd', - \ 'pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd'] + \ 'postgrep', + \ 'pregrepadd', + \ 'postgrepadd', + \ 'pregrep', + \ 'postgrep', + \ 'pregrepadd', + \ 'postgrepadd', + \ 'pregrep', + \ 'postgrep', + \ 'pregrepadd', + \ 'postgrepadd', + \ 'prelgrep', + \ 'postlgrep', + \ 'prelgrepadd', + \ 'postlgrepadd'] call assert_equal(l, g:acmds) endif @@ -2491,6 +2575,19 @@ func Test_cwindow_jump() call assert_true(winnr('$') == 2) call assert_true(winnr() == 1) + " Jumping to a file from the location list window should find a usuable + " window by wrapping around the window list. + enew | only + call setloclist(0, [], 'f') + new | new + lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"] + lopen + 1close + call assert_equal(0, getloclist(3, {'id' : 0}).id) + lnext + call assert_equal(3, winnr()) + call assert_equal(getloclist(1, {'id' : 0}).id, getloclist(3, {'id' : 0}).id) + enew | only set efm&vim endfunc @@ -3762,6 +3859,30 @@ func Test_qftitle() call setqflist([], 'r', {'items' : [{'filename' : 'a.c', 'lnum' : 10}]}) call assert_equal('Errors', w:quickfix_title) cclose + + " Switching to another quickfix list in one tab page should update the + " quickfix window title and statusline in all the other tab pages also + call setqflist([], 'f') + %bw! + cgetexpr ['file_one:1:1: error in the first quickfix list'] + call setqflist([], 'a', {'title': 'first quickfix list'}) + cgetexpr ['file_two:2:1: error in the second quickfix list'] + call setqflist([], 'a', {'title': 'second quickfix list'}) + copen + wincmd t + tabnew two + copen + wincmd t + colder + call assert_equal('first quickfix list', gettabwinvar(1, 2, 'quickfix_title')) + call assert_equal('first quickfix list', gettabwinvar(2, 2, 'quickfix_title')) + call assert_equal(1, tabpagewinnr(1)) + call assert_equal(1, tabpagewinnr(2)) + tabnew + call setqflist([], 'a', {'title': 'new quickfix title'}) + call assert_equal('new quickfix title', gettabwinvar(1, 2, 'quickfix_title')) + call assert_equal('new quickfix title', gettabwinvar(2, 2, 'quickfix_title')) + %bw! endfunc func Test_lbuffer_with_bwipe() @@ -4177,17 +4298,22 @@ func Test_empty_qfbuf() endfunc " Test for the :cbelow, :cabove, :lbelow and :labove commands. +" And for the :cafter, :cbefore, :lafter and :lbefore commands. func Xtest_below(cchar) call s:setup_commands(a:cchar) " No quickfix/location list call assert_fails('Xbelow', 'E42:') call assert_fails('Xabove', 'E42:') + call assert_fails('Xbefore', 'E42:') + call assert_fails('Xafter', 'E42:') " Empty quickfix/location list call g:Xsetlist([]) call assert_fails('Xbelow', 'E42:') call assert_fails('Xabove', 'E42:') + call assert_fails('Xbefore', 'E42:') + call assert_fails('Xafter', 'E42:') call s:create_test_file('X1') call s:create_test_file('X2') @@ -4201,39 +4327,74 @@ func Xtest_below(cchar) call assert_fails('Xabove', 'E42:') call assert_fails('3Xbelow', 'E42:') call assert_fails('4Xabove', 'E42:') + call assert_fails('Xbefore', 'E42:') + call assert_fails('Xafter', 'E42:') + call assert_fails('3Xbefore', 'E42:') + call assert_fails('4Xafter', 'E42:') " Test the commands with various arguments - Xexpr ["X1:5:L5", "X2:5:L5", "X2:10:L10", "X2:15:L15", "X3:3:L3"] + Xexpr ["X1:5:3:L5", "X2:5:2:L5", "X2:10:3:L10", "X2:15:4:L15", "X3:3:5:L3"] edit +7 X2 Xabove call assert_equal(['X2', 5], [bufname(''), line('.')]) call assert_fails('Xabove', 'E553:') + normal 7G + Xbefore + call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')]) + call assert_fails('Xbefore', 'E553:') + normal 2j Xbelow call assert_equal(['X2', 10], [bufname(''), line('.')]) + normal 7G + Xafter + call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')]) + " Last error in this file Xbelow 99 call assert_equal(['X2', 15], [bufname(''), line('.')]) call assert_fails('Xbelow', 'E553:') + normal gg + Xafter 99 + call assert_equal(['X2', 15, 4], [bufname(''), line('.'), col('.')]) + call assert_fails('Xafter', 'E553:') + " First error in this file Xabove 99 call assert_equal(['X2', 5], [bufname(''), line('.')]) call assert_fails('Xabove', 'E553:') + normal G + Xbefore 99 + call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')]) + call assert_fails('Xbefore', 'E553:') + normal gg Xbelow 2 call assert_equal(['X2', 10], [bufname(''), line('.')]) + normal gg + Xafter 2 + call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')]) + normal G Xabove 2 call assert_equal(['X2', 10], [bufname(''), line('.')]) + normal G + Xbefore 2 + call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')]) + edit X4 call assert_fails('Xabove', 'E42:') call assert_fails('Xbelow', 'E42:') + call assert_fails('Xbefore', 'E42:') + call assert_fails('Xafter', 'E42:') if a:cchar == 'l' " If a buffer has location list entries from some other window but not " from the current window, then the commands should fail. edit X1 | split | call setloclist(0, [], 'f') call assert_fails('Xabove', 'E776:') call assert_fails('Xbelow', 'E776:') + call assert_fails('Xbefore', 'E776:') + call assert_fails('Xafter', 'E776:') close endif @@ -4244,31 +4405,52 @@ func Xtest_below(cchar) edit +1 X2 Xbelow 2 call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')]) + normal 1G + Xafter 2 + call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')]) + normal gg Xbelow 99 call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')]) + normal gg + Xafter 99 + call assert_equal(['X2', 15, 3], [bufname(''), line('.'), col('.')]) + normal G Xabove 2 call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')]) normal G + Xbefore 2 + call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')]) + + normal G Xabove 99 call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) + normal G + Xbefore 99 + call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) + normal 10G Xabove call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) + normal 10G$ + 2Xbefore + call assert_equal(['X2', 10, 2], [bufname(''), line('.'), col('.')]) + normal 10G Xbelow call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')]) + normal 9G + 5Xafter + call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')]) " Invalid range if a:cchar == 'c' - call assert_fails('-2cbelow', 'E553:') - " TODO: should go to first error in the current line? - 0cabove + call assert_fails('-2cbelow', 'E16:') + call assert_fails('-2cafter', 'E16:') else - call assert_fails('-2lbelow', 'E553:') - " TODO: should go to first error in the current line? - 0labove + call assert_fails('-2lbelow', 'E16:') + call assert_fails('-2lafter', 'E16:') endif call delete('X1') @@ -4282,6 +4464,42 @@ func Test_cbelow() call Xtest_below('l') endfunc +func Test_quickfix_count() + let commands = [ + \ 'cNext', + \ 'cNfile', + \ 'cabove', + \ 'cbelow', + \ 'cfirst', + \ 'clast', + \ 'cnewer', + \ 'cnext', + \ 'cnfile', + \ 'colder', + \ 'cprevious', + \ 'crewind', + \ + \ 'lNext', + \ 'lNfile', + \ 'labove', + \ 'lbelow', + \ 'lfirst', + \ 'llast', + \ 'lnewer', + \ 'lnext', + \ 'lnfile', + \ 'lolder', + \ 'lprevious', + \ 'lrewind', + \ ] + for cmd in commands + call assert_fails('-1' .. cmd, 'E16:') + call assert_fails('.' .. cmd, 'E16:') + call assert_fails('%' .. cmd, 'E16:') + call assert_fails('$' .. cmd, 'E16:') + endfor +endfunc + " Test for aborting quickfix commands using QuickFixCmdPre func Xtest_qfcmd_abort(cchar) call s:setup_commands(a:cchar) @@ -4437,6 +4655,24 @@ func Test_cquit() call assert_fails('-3cquit', 'E16:') endfunc +" Running :lhelpgrep command more than once in a help window, doesn't jump to +" the help topic +func Test_lhelpgrep_from_help_window() + call mkdir('Xtestdir/doc', 'p') + call writefile(['window'], 'Xtestdir/doc/a.txt') + call writefile(['buffer'], 'Xtestdir/doc/b.txt') + let save_rtp = &rtp + let &rtp = 'Xtestdir' + lhelpgrep window + lhelpgrep buffer + call assert_equal('b.txt', fnamemodify(@%, ":p:t")) + lhelpgrep window + call assert_equal('a.txt', fnamemodify(@%, ":p:t")) + let &rtp = save_rtp + call delete('Xtestdir', 'rf') + new | only! +endfunc + " Test for adding an invalid entry with the quickfix window open and making " sure that the window contents are not changed func Test_add_invalid_entry_with_qf_window() diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 2ee0ee1c0c..1bb2ee53de 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -755,6 +755,13 @@ func Test_start_end_of_buffer_match() bwipe! endfunc +func Test_ze_before_zs() + call assert_equal('', matchstr(' ', '\%#=1\ze \zs')) + call assert_equal('', matchstr(' ', '\%#=2\ze \zs')) + call assert_equal(repeat([''], 10), matchlist(' ', '\%#=1\ze \zs')) + call assert_equal(repeat([''], 10), matchlist(' ', '\%#=2\ze \zs')) +endfunc + " Check for detecting error func Test_regexp_error() set regexpengine=2 diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index 4466ad436a..513780938e 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -533,4 +533,15 @@ func Test_search_with_end_offset() close! endfunc +" Check that "^" matches even when the line starts with a combining char +func Test_match_start_of_line_combining() + new + call setline(1, ['', "\u05ae", '']) + exe "normal gg/^\<CR>" + call assert_equal(2, getcurpos()[1]) + bwipe! +endfunc + + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 19a7c6c9d0..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() @@ -254,4 +270,15 @@ func Test_ve_blockpaste() bwipe! endfunc +func Test_insert_small_delete() + new + call setline(1, ['foo foobar bar']) + call cursor(1,1) + exe ":norm! ciw'\<C-R>-'" + call assert_equal("'foo' foobar bar", getline(1)) + exe ":norm! w.w." + call assert_equal("'foo' 'foobar' 'bar'", getline(1)) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ruby.vim b/src/nvim/testdir/test_ruby.vim index 9c74c35049..1a274d1fec 100644 --- a/src/nvim/testdir/test_ruby.vim +++ b/src/nvim/testdir/test_ruby.vim @@ -27,6 +27,19 @@ func Test_rubydo() %bwipe! endfunc +func Test_rubydo_dollar_underscore() + throw 'skipped: TODO: ' + new + call setline(1, ['one', 'two', 'three', 'four']) + 2,3rubydo $_ = '[' + $_ + ']' + call assert_equal(['one', '[two]', '[three]', 'four'], getline(1, '$')) + bwipe! + + call assert_fails('rubydo $_ = 0', 'E265:') + call assert_fails('rubydo (') + bwipe! +endfunc + func Test_rubyfile() " Check :rubyfile does not SEGV with Ruby level exception but just fails let tempfile = tempname() . '.rb' @@ -391,3 +404,14 @@ func Test_ruby_p() let messages = GetMessages() call assert_equal(0, len(messages)) endfunc + +func Test_rubyeval_error() + " On Linux or Windows the error matches: + " "syntax error, unexpected end-of-input" + " whereas on macOS in CI, the error message makes less sense: + " "SyntaxError: array length must be 2" + " Unclear why. The test does not check the error message. + call assert_fails('call rubyeval("(")') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 6824c50112..0703a6b141 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -270,7 +270,7 @@ func Test_search_cmdline2() " nor "/foo\<c-u>\<cr>" works to delete the commandline. " In that case Vim should return "E35 no previous regular expression", " but it looks like Vim still sees /foo and therefore the test fails. - " Therefore, disableing this test + " Therefore, disabling this test "call assert_fails(feedkeys("/foo\<c-w>\<cr>", 'tx'), 'E35') "call assert_equal({'lnum': 1, 'leftcol': 0, 'col': 0, 'topfill': 0, 'topline': 1, 'coladd': 0, 'skipcol': 0, 'curswant': 0}, winsaveview()) @@ -508,7 +508,7 @@ endfunc func Test_search_cmdline7() throw 'skipped: Nvim does not support test_override()' - " Test that an pressing <c-g> in an empty command line + " Test that pressing <c-g> in an empty command line " does not move the cursor if !exists('+incsearch') return @@ -1172,3 +1172,24 @@ func Test_search_special() set t_PE= exe "norm /\x80PS" endfunc + +" Test 'smartcase' with utf-8. +func Test_search_smartcase_utf8() + new + let save_enc = &encoding + set encoding=utf8 ignorecase smartcase + + call setline(1, 'Café cafÉ') + 1s/café/x/g + call assert_equal('x x', getline(1)) + + call setline(1, 'Café cafÉ') + 1s/cafÉ/x/g + call assert_equal('Café x', getline(1)) + + set ignorecase& smartcase& + let &encoding = save_enc + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim new file mode 100644 index 0000000000..338c0d79ff --- /dev/null +++ b/src/nvim/testdir/test_signals.vim @@ -0,0 +1,140 @@ +" Test signal handling. + +source check.vim +source term_util.vim + +CheckUnix + +source shared.vim + +" Check whether a signal is available on this system. +func HasSignal(signal) + let signals = system('kill -l') + return signals =~# '\<' .. a:signal .. '\>' +endfunc + +" Test signal WINCH (window resize signal) +func Test_signal_WINCH() + throw 'skipped: Nvim cannot avoid terminal resize' + if has('gui_running') || !HasSignal('WINCH') + return + endif + + " We do not actually want to change the size of the terminal. + let old_WS = '' + if exists('&t_WS') + let old_WS = &t_WS + let &t_WS = '' + endif + + let old_lines = &lines + let old_columns = &columns + let new_lines = &lines - 2 + let new_columns = &columns - 2 + + exe 'set lines=' .. new_lines + exe 'set columns=' .. new_columns + call assert_equal(new_lines, &lines) + call assert_equal(new_columns, &columns) + + " Send signal and wait for signal to be processed. + " 'lines' and 'columns' should have been restored + " after handing signal WINCH. + exe 'silent !kill -s WINCH ' .. getpid() + call WaitForAssert({-> assert_equal(old_lines, &lines)}) + call assert_equal(old_columns, &columns) + + if old_WS != '' + let &t_WS = old_WS + endif +endfunc + +" Test signal PWR, which should update the swap file. +func Test_signal_PWR() + if !HasSignal('PWR') + return + endif + + " Set a very large 'updatetime' and 'updatecount', so that we can be sure + " that swap file is updated as a result of sending PWR signal, and not + " because of exceeding 'updatetime' or 'updatecount' when changing buffer. + set updatetime=100000 updatecount=100000 + new Xtest_signal_PWR + let swap_name = swapname('%') + call setline(1, '123') + preserve + let swap_content = readfile(swap_name, 'b') + + " Update the buffer and check that the swap file is not yet updated, + " since we set 'updatetime' and 'updatecount' to large values. + call setline(1, 'abc') + call assert_equal(swap_content, readfile(swap_name, 'b')) + + " Sending PWR signal should update the swap file. + exe 'silent !kill -s PWR ' .. getpid() + call WaitForAssert({-> assert_notequal(swap_content, readfile(swap_name, 'b'))}) + + bwipe! + set updatetime& updatecount& +endfunc + +" Test a deadly signal. +" +" There are several deadly signals: SISEGV, SIBUS, SIGTERM... +" Test uses signal SIGTERM as it does not create a core +" dump file unlike SIGSEGV, SIGBUS, etc. See "man 7 signals. +" +" Vim should exit with a deadly signal and unsaved changes +" should be recoverable from the swap file preserved as a +" result of the deadly signal handler. +func Test_deadly_signal_TERM() + if !HasSignal('TERM') + throw 'Skipped: TERM signal not supported' + endif + if !CanRunVimInTerminal() + throw 'Skipped: cannot run vim in terminal' + endif + let cmd = GetVimCommand() + if cmd =~ 'valgrind' + throw 'Skipped: cannot test signal TERM with valgrind' + endif + + " If test fails once, it can leave temporary files and trying to rerun + " the test would then fail again if they are not deleted first. + call delete('.Xsig_TERM.swp') + call delete('XsetupAucmd') + call delete('XautoOut') + let lines =<< trim END + au VimLeave * call writefile(["VimLeave triggered"], "XautoOut", "as") + au VimLeavePre * call writefile(["VimLeavePre triggered"], "XautoOut", "as") + END + call writefile(lines, 'XsetupAucmd') + + let buf = RunVimInTerminal('-S XsetupAucmd Xsig_TERM', {'rows': 6}) + let pid_vim = term_getjob(buf)->job_info().process + + call term_sendkeys(buf, ":call setline(1, 'foo')\n") + call WaitForAssert({-> assert_equal('foo', term_getline(buf, 1))}) + + call assert_false(filereadable('Xsig_TERM')) + exe 'silent !kill -s TERM ' .. pid_vim + call WaitForAssert({-> assert_true(filereadable('.Xsig_TERM.swp'))}) + + " Don't call StopVimInTerminal() as it expects job to be still running. + call WaitForAssert({-> assert_equal("finished", term_getstatus(buf))}) + + new + silent recover .Xsig_TERM.swp + call assert_equal(['foo'], getline(1, '$')) + + let result = readfile('XautoOut') + call assert_match('VimLeavePre triggered', result[0]) + call assert_match('VimLeave triggered', result[1]) + + %bwipe! + call delete('.Xsig_TERM.swp') + call delete('XsetupAucmd') + call delete('XautoOut') +endfunc + +" vim: ts=8 sw=2 sts=2 tw=80 fdm=marker diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 2b7672f1af..4bbd722fdb 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -15,14 +15,8 @@ func Test_sign() " icon is ignored when not supported. "(not supported)" is shown after " the icon name when listing signs. sign define Sign1 text=x - try - sign define Sign2 text=xy texthl=Title linehl=Error - \ icon=../../pixmaps/stock_vim_find_help.png - catch /E255:/ - " Ignore error: E255: Couldn't read in sign data! - " This error can happen when running in the GUI. - " Some gui like Motif do not support the png icon format. - endtry + + call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error icon=../../pixmaps/stock_vim_find_help.png') " Test listing signs. let a=execute('sign list') @@ -104,12 +98,7 @@ func Test_sign() edit foo call setline(1, ['A', 'B', 'C', 'D']) - try - sign define Sign3 text=y texthl=DoesNotExist linehl=DoesNotExist - \ icon=doesnotexist.xpm - catch /E255:/ - " ignore error: E255: it can happens for guis. - endtry + call Sign_command_ignore_error('sign define Sign3 text=y texthl=DoesNotExist linehl=DoesNotExist icon=doesnotexist.xpm') let fn = expand('%:p') exe 'sign place 43 line=2 name=Sign3 file=' . fn @@ -133,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 @@ -317,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:') @@ -378,6 +367,25 @@ func Test_sign_delete_buffer() sign undefine Sign endfunc +" Ignore error: E255: Couldn't read in sign data! +" This error can happen when running in the GUI. +" Some gui like Motif do not support the png icon format. +func Sign_command_ignore_error(cmd) + try + exe a:cmd + catch /E255:/ + endtry +endfunc + +" ignore error: E255: Couldn't read in sign data! +" This error can happen when running in gui. +func Sign_define_ignore_error(name, attr) + try + call sign_define(a:name, a:attr) + catch /E255:/ + endtry +endfunc + " Test for Vim script functions for managing signs func Test_sign_funcs() " Remove all the signs @@ -394,12 +402,7 @@ func Test_sign_funcs() call sign_define("sign2") let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange', \ 'icon' : 'sign2.ico'} - try - call sign_define("sign2", attr) - catch /E255:/ - " ignore error: E255: Couldn't read in sign data! - " This error can happen when running in gui. - endtry + call Sign_define_ignore_error("sign2", attr) call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange', \ 'linehl' : 'DiffAdd', 'text' : '!!', 'icon' : 'sign2.ico'}], \ sign_getdefined("sign2")) @@ -412,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:') @@ -508,6 +511,16 @@ func Test_sign_funcs() call assert_fails('call sign_undefine("none")', 'E155:') call assert_fails('call sign_undefine([])', 'E730:') + " Test for using '.' as the line number for sign_place() + call Sign_define_ignore_error("sign1", attr) + call cursor(22, 1) + call assert_equal(15, sign_place(15, '', 'sign1', 'Xsign', + \ {'lnum' : '.'})) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 15, 'group' : '', 'lnum' : 22, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced('%', {'lnum' : 22})) + call delete("Xsign") call sign_unplace('*') call sign_undefine() diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 6214975ef5..e7f332bc4c 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -686,3 +686,53 @@ func Test_v_argv() call assert_true(idx > 2) call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:]) endfunc + +" Test the '-T' argument which sets the 'term' option. +func Test_T_arg() + throw 'skipped: Nvim does not support "-T" argument' + CheckNotGui + let after =<< trim [CODE] + call writefile([&term], "Xtest_T_arg") + qall + [CODE] + + for t in ['builtin_dumb', 'builtin_ansi'] + if RunVim([], after, '-T ' .. t) + let lines = readfile('Xtest_T_arg') + call assert_equal([t], lines) + endif + endfor + + call delete('Xtest_T_arg') +endfunc + +" Test the '-x' argument to read/write encrypted files. +func Test_x_arg() + CheckRunVimInTerminal + CheckFeature cryptv + + " Create an encrypted file Xtest_x_arg. + let buf = RunVimInTerminal('-n -x Xtest_x_arg', #{rows: 10, wait_for_ruler: 0}) + call WaitForAssert({-> assert_match('^Enter encryption key: ', term_getline(buf, 10))}) + call term_sendkeys(buf, "foo\n") + call WaitForAssert({-> assert_match('^Enter same key again: ', term_getline(buf, 10))}) + call term_sendkeys(buf, "foo\n") + call WaitForAssert({-> assert_match(' All$', term_getline(buf, 10))}) + call term_sendkeys(buf, "itest\<Esc>:w\<Enter>") + call WaitForAssert({-> assert_match('"Xtest_x_arg" \[New\]\[blowfish2\] 1L, 5B written', + \ term_getline(buf, 10))}) + call StopVimInTerminal(buf) + + " Read the encrypted file and check that it contains the expected content "test" + let buf = RunVimInTerminal('-n -x Xtest_x_arg', #{rows: 10, wait_for_ruler: 0}) + call WaitForAssert({-> assert_match('^Enter encryption key: ', term_getline(buf, 10))}) + call term_sendkeys(buf, "foo\n") + call WaitForAssert({-> assert_match('^Enter same key again: ', term_getline(buf, 10))}) + call term_sendkeys(buf, "foo\n") + call WaitForAssert({-> assert_match('^test', term_getline(buf, 1))}) + call StopVimInTerminal(buf) + + call delete('Xtest_x_arg') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 424cb4abd0..9cf8690d57 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -3,7 +3,7 @@ source shared.vim source check.vim -function! Test_System() +func Test_System() if !executable('echo') || !executable('cat') || !executable('wc') return endif diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 55dff3d476..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! @@ -58,7 +59,7 @@ function Test_tabpage() q " " - " Test for ":tab drop multi-opend-file" to keep current tabpage and window. + " Test for ":tab drop multi-opened-file" to keep current tabpage and window. new test1 tabnew new test1 @@ -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_textformat.vim b/src/nvim/testdir/test_textformat.vim index 2223be952c..4af52b536c 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -424,6 +424,15 @@ func Test_format_align() \ ], getline(1, '$')) enew! + " align text with 'wrapmargin' + 50vnew + call setline(1, ['Vim']) + setl textwidth=0 + setl wrapmargin=30 + right + call assert_equal("\t\t Vim", getline(1)) + q! + set tw& endfunc @@ -931,4 +940,217 @@ func Test_substitute() call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g')) endfunc +" Test for 'a' and 'w' flags in 'formatoptions' +func Test_fo_a_w() + new + setlocal fo+=aw tw=10 + call feedkeys("iabc abc a abc\<Esc>k0weade", 'xt') + call assert_equal(['abc abcde ', 'a abc'], getline(1, '$')) + + " Test for 'a', 'w' and '1' options. + setlocal textwidth=0 + setlocal fo=1aw + %d + call setline(1, '. foo') + normal 72ig + call feedkeys('a uu uu uu', 'xt') + call assert_equal('g uu uu ', getline(1)[-8:]) + call assert_equal(['uu. foo'], getline(2, '$')) + + " using backspace or "x" triggers reformat + call setline(1, ['1 2 3 4 5 ', '6 7 8 9']) + set tw=10 + set fo=taw + set bs=indent,eol,start + exe "normal 1G4la\<BS>\<BS>\<Esc>" + call assert_equal(['1 2 4 5 6 ', '7 8 9'], getline(1, 2)) + exe "normal f4xx" + call assert_equal(['1 2 5 6 7 ', '8 9'], getline(1, 2)) + + set tw=0 + set fo& + %bw! +endfunc + +" Test for formatting lines using gq in visual mode +func Test_visual_gq_format() + new + call setline(1, ['one two three four', 'five six', 'one two']) + setl textwidth=10 + call feedkeys('ggv$jj', 'xt') + redraw! + normal gq + %d + call setline(1, ['one two three four', 'five six', 'one two']) + normal G$ + call feedkeys('v0kk', 'xt') + redraw! + normal gq + setl textwidth& + close! +endfunc + +" Test for 'n' flag in 'formatoptions' to format numbered lists +func Test_fo_n() + new + setlocal autoindent + setlocal textwidth=12 + setlocal fo=n + call setline(1, [' 1) one two three four', ' 2) two']) + normal gggqG + call assert_equal([' 1) one two', ' three', ' four', ' 2) two'], + \ getline(1, '$')) + close! +endfunc + +" Test for 'formatlistpat' option +func Test_formatlistpat() + new + setlocal autoindent + setlocal textwidth=10 + setlocal fo=n + setlocal formatlistpat=^\\s*-\\s* + call setline(1, [' - one two three', ' - two']) + normal gggqG + call assert_equal([' - one', ' two', ' three', ' - two'], + \ getline(1, '$')) + close! +endfunc + +" Test for the 'b' and 'v' flags in 'formatoptions' +" Text should wrap only if a space character is inserted at or before +" 'textwidth' +func Test_fo_b() + new + setlocal textwidth=20 + + setlocal formatoptions=t + call setline(1, 'one two three four') + call feedkeys('Amore', 'xt') + call assert_equal(['one two three', 'fourmore'], getline(1, '$')) + + setlocal formatoptions=bt + %d + call setline(1, 'one two three four') + call feedkeys('Amore five', 'xt') + call assert_equal(['one two three fourmore five'], getline(1, '$')) + + setlocal formatoptions=bt + %d + call setline(1, 'one two three four') + call feedkeys('A five', 'xt') + call assert_equal(['one two three four', 'five'], getline(1, '$')) + + setlocal formatoptions=vt + %d + call setline(1, 'one two three four') + call feedkeys('Amore five', 'xt') + call assert_equal(['one two three fourmore', 'five'], getline(1, '$')) + + close! +endfunc + +" Test for the '1' flag in 'formatoptions'. Don't wrap text after a one letter +" word. +func Test_fo_1() + new + setlocal textwidth=20 + + setlocal formatoptions=t + call setline(1, 'one two three four') + call feedkeys('A a bird', 'xt') + call assert_equal(['one two three four a', 'bird'], getline(1, '$')) + + %d + setlocal formatoptions=t1 + call setline(1, 'one two three four') + call feedkeys('A a bird', 'xt') + call assert_equal(['one two three four', 'a bird'], getline(1, '$')) + + close! +endfunc + +" Test for 'l' flag in 'formatoptions'. When starting insert mode, if a line +" is longer than 'textwidth', then it is not broken. +func Test_fo_l() + new + setlocal textwidth=20 + + setlocal formatoptions=t + call setline(1, 'one two three four five') + call feedkeys('A six', 'xt') + call assert_equal(['one two three four', 'five six'], getline(1, '$')) + + %d + setlocal formatoptions=tl + call setline(1, 'one two three four five') + call feedkeys('A six', 'xt') + call assert_equal(['one two three four five six'], getline(1, '$')) + + close! +endfunc + +" Test for the '2' flag in 'formatoptions' +func Test_fo_2() + new + setlocal autoindent + setlocal formatoptions=t2 + setlocal textwidth=30 + call setline(1, ["\tfirst line of a paragraph.", + \ "second line of the same paragraph.", + \ "third line."]) + normal gggqG + call assert_equal(["\tfirst line of a", + \ "paragraph. second line of the", + \ "same paragraph. third line."], getline(1, '$')) + close! +endfunc + +" Test for formatting lines where only the first line has a comment. +func Test_fo_gq_with_firstline_comment() + new + setlocal formatoptions=tcq + call setline(1, ['- one two', 'three']) + normal gggqG + call assert_equal(['- one two three'], getline(1, '$')) + + %d + call setline(1, ['- one', '- two']) + normal gggqG + call assert_equal(['- one', '- two'], getline(1, '$')) + close! +endfunc + +" Test for trying to join a comment line with a non-comment line +func Test_join_comments() + new + call setline(1, ['one', '/* two */', 'three']) + normal gggqG + call assert_equal(['one', '/* two */', 'three'], getline(1, '$')) + close! +endfunc + +" Test for using 'a' in 'formatoptions' with comments +func Test_autoformat_comments() + new + setlocal formatoptions+=a + call feedkeys("a- one\n- two\n", 'xt') + call assert_equal(['- one', '- two', ''], getline(1, '$')) + + %d + call feedkeys("a\none\n", 'xt') + call assert_equal(['', 'one', ''], getline(1, '$')) + + setlocal formatoptions+=aw + %d + call feedkeys("aone \ntwo\n", 'xt') + call assert_equal(['one two', ''], getline(1, '$')) + + %d + call feedkeys("aone\ntwo\n", 'xt') + call assert_equal(['one', 'two', ''], getline(1, '$')) + + close! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim new file mode 100644 index 0000000000..7e513180a3 --- /dev/null +++ b/src/nvim/testdir/test_trycatch.vim @@ -0,0 +1,1977 @@ +" Test try-catch-finally exception handling +" Most of this was formerly in test49. + +source check.vim +source shared.vim + +"------------------------------------------------------------------------------- +" Test environment {{{1 +"------------------------------------------------------------------------------- + +com! XpathINIT let g:Xpath = '' +com! -nargs=1 -bar Xpath let g:Xpath = g:Xpath . <args> + +" Test 25: Executing :finally clauses on normal control flow {{{1 +" +" Control flow in a :try conditional should always fall through to its +" :finally clause. A :finally clause of a :try conditional inside an +" inactive conditional should never be executed. +"------------------------------------------------------------------------------- + +func T25_F() + let loops = 3 + while loops > 0 + Xpath 'a' . loops + if loops >= 2 + try + Xpath 'b' . loops + if loops == 2 + try + Xpath 'c' . loops + finally + Xpath 'd' . loops + endtry + endif + finally + Xpath 'e' . loops + if loops == 2 + try + Xpath 'f' . loops + finally + Xpath 'g' . loops + endtry + endif + endtry + endif + Xpath 'h' . loops + let loops = loops - 1 + endwhile + Xpath 'i' +endfunc + +func T25_G() + if 1 + try + Xpath 'A' + call T25_F() + Xpath 'B' + finally + Xpath 'C' + endtry + else + try + Xpath 'D' + finally + Xpath 'E' + endtry + endif +endfunc + +func Test_finally() + XpathINIT + call T25_G() + call assert_equal('Aa3b3e3h3a2b2c2d2e2f2g2h2a1h1iBC', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 26: Executing :finally clauses after :continue or :break {{{1 +" +" For a :continue or :break dynamically enclosed in a :try/:endtry +" region inside the next surrounding :while/:endwhile, if the +" :continue/:break is before the :finally, the :finally clause is +" executed first. If the :continue/:break is after the :finally, the +" :finally clause is broken (like an :if/:endif region). +"------------------------------------------------------------------------------- + +func T26_F() + try + let loops = 3 + while loops > 0 + try + try + if loops == 2 + Xpath 'a' . loops + let loops = loops - 1 + continue + elseif loops == 1 + Xpath 'b' . loops + break + finish + endif + Xpath 'c' . loops + endtry + finally + Xpath 'd' . loops + endtry + Xpath 'e' . loops + let loops = loops - 1 + endwhile + Xpath 'f' + finally + Xpath 'g' + let loops = 3 + while loops > 0 + try + finally + try + if loops == 2 + Xpath 'h' . loops + let loops = loops - 1 + continue + elseif loops == 1 + Xpath 'i' . loops + break + finish + endif + endtry + Xpath 'j' . loops + endtry + Xpath 'k' . loops + let loops = loops - 1 + endwhile + Xpath 'l' + endtry + Xpath 'm' +endfunc + +func Test_finally_after_continue() + XpathINIT + call T26_F() + call assert_equal('c3d3e3a2d1b1d1fgj3k3h2i1lm', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 32: Remembering the :return value on :finally {{{1 +" +" If a :finally clause is executed due to a :return specifying +" a value, this is the value visible to the caller if not overwritten +" by a new :return in the :finally clause. A :return without a value +" in the :finally clause overwrites with value 0. +"------------------------------------------------------------------------------- + +func T32_F() + try + Xpath 'a' + try + Xpath 'b' + return "ABCD" + Xpath 'c' + finally + Xpath 'd' + endtry + Xpath 'e' + finally + Xpath 'f' + endtry + Xpath 'g' +endfunc + +func T32_G() + try + Xpath 'h' + return 8 + Xpath 'i' + finally + Xpath 'j' + return 16 + strlen(T32_F()) + Xpath 'k' + endtry + Xpath 'l' +endfunc + +func T32_H() + try + Xpath 'm' + return 32 + Xpath 'n' + finally + Xpath 'o' + return + Xpath 'p' + endtry + Xpath 'q' +endfunc + +func T32_I() + try + Xpath 'r' + finally + Xpath 's' + return T32_G() + T32_H() + 64 + Xpath 't' + endtry + Xpath 'u' +endfunc + +func Test_finally_return() + XpathINIT + call assert_equal(84, T32_I()) + call assert_equal('rshjabdfmo', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 33: :return under :execute or user command and :finally {{{1 +" +" A :return command may be executed under an ":execute" or from +" a user command. Executing of :finally clauses and passing through +" the return code works also then. +"------------------------------------------------------------------------------- + +func T33_F() + try + RETURN 10 + Xpath 'a' + finally + Xpath 'b' + endtry + Xpath 'c' +endfunc + +func T33_G() + try + RETURN 20 + Xpath 'd' + finally + Xpath 'e' + RETURN 30 + Xpath 'f' + endtry + Xpath 'g' +endfunc + +func T33_H() + try + execute "try | return 40 | finally | return 50 | endtry" + Xpath 'h' + finally + Xpath 'i' + endtry + Xpath 'j' +endfunc + +func T33_I() + try + execute "try | return 60 | finally | return 70 | endtry" + Xpath 'k' + finally + Xpath 'l' + execute "try | return 80 | finally | return 90 | endtry" + Xpath 'm' + endtry + Xpath 'n' +endfunc + +func T33_J() + try + RETURN 100 + Xpath 'o' + finally + Xpath 'p' + return + Xpath 'q' + endtry + Xpath 'r' +endfunc + +func T33_K() + try + execute "try | return 110 | finally | return 120 | endtry" + Xpath 's' + finally + Xpath 't' + execute "try | return 130 | finally | return | endtry" + Xpath 'u' + endtry + Xpath 'v' +endfunc + +func T33_L() + try + return + Xpath 'w' + finally + Xpath 'x' + RETURN 140 + Xpath 'y' + endtry + Xpath 'z' +endfunc + +func T33_M() + try + return + Xpath 'A' + finally + Xpath 'B' + execute "try | return 150 | finally | return 160 | endtry" + Xpath 'C' + endtry + Xpath 'D' +endfunc + +func T33_N() + RETURN 170 +endfunc + +func T33_O() + execute "try | return 180 | finally | return 190 | endtry" +endfunc + +func Test_finally_cmd_return() + command! -nargs=? RETURN + \ try | return <args> | finally | return <args> * 2 | endtry + XpathINIT + call assert_equal(20, T33_F()) + call assert_equal(60, T33_G()) + call assert_equal(50, T33_H()) + call assert_equal(90, T33_I()) + call assert_equal(0, T33_J()) + call assert_equal(0, T33_K()) + call assert_equal(280, T33_L()) + call assert_equal(160, T33_M()) + call assert_equal(340, T33_N()) + call assert_equal(190, T33_O()) + call assert_equal('beilptxB', g:Xpath) + delcommand RETURN +endfunc + + +"------------------------------------------------------------------------------- +" Test 41: Skipped :throw finding next command {{{1 +" +" A :throw in an inactive conditional must not hide a following +" command. +"------------------------------------------------------------------------------- + +func T41_F() + Xpath 'a' + if 0 | throw 'never' | endif | Xpath 'b' + Xpath 'c' +endfunc + +func T41_G() + Xpath 'd' + while 0 | throw 'never' | endwhile | Xpath 'e' + Xpath 'f' +endfunc + +func T41_H() + Xpath 'g' + if 0 | try | throw 'never' | endtry | endif | Xpath 'h' + Xpath 'i' +endfunc + +func Test_throw_inactive_cond() + XpathINIT + try + Xpath 'j' + call T41_F() + Xpath 'k' + catch /.*/ + Xpath 'l' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 'm' + call T41_G() + Xpath 'n' + catch /.*/ + Xpath 'o' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 'p' + call T41_H() + Xpath 'q' + catch /.*/ + Xpath 'r' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('jabckmdefnpghiq', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 42: Catching number and string exceptions {{{1 +" +" When a number is thrown, it is converted to a string exception. +" Numbers and strings may be caught by specifying a regular exception +" as argument to the :catch command. +"------------------------------------------------------------------------------- + + +func T42_F() + try + + try + Xpath 'a' + throw 4711 + Xpath 'b' + catch /4711/ + Xpath 'c' + endtry + + try + Xpath 'd' + throw 4711 + Xpath 'e' + catch /^4711$/ + Xpath 'f' + endtry + + try + Xpath 'g' + throw 4711 + Xpath 'h' + catch /\d/ + Xpath 'i' + endtry + + try + Xpath 'j' + throw 4711 + Xpath 'k' + catch /^\d\+$/ + Xpath 'l' + endtry + + try + Xpath 'm' + throw "arrgh" + Xpath 'n' + catch /arrgh/ + Xpath 'o' + endtry + + try + Xpath 'p' + throw "arrgh" + Xpath 'q' + catch /^arrgh$/ + Xpath 'r' + endtry + + try + Xpath 's' + throw "arrgh" + Xpath 't' + catch /\l/ + Xpath 'u' + endtry + + try + Xpath 'v' + throw "arrgh" + Xpath 'w' + catch /^\l\+$/ + Xpath 'x' + endtry + + try + try + Xpath 'y' + throw "ARRGH" + Xpath 'z' + catch /^arrgh$/ + Xpath 'A' + endtry + catch /^\carrgh$/ + Xpath 'B' + endtry + + try + Xpath 'C' + throw "" + Xpath 'D' + catch /^$/ + Xpath 'E' + endtry + + catch /.*/ + Xpath 'F' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry +endfunc + +func Test_catch_number_string() + XpathINIT + call T42_F() + call assert_equal('acdfgijlmoprsuvxyBCE', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 43: Selecting the correct :catch clause {{{1 +" +" When an exception is thrown and there are multiple :catch clauses, +" the first matching one is taken. +"------------------------------------------------------------------------------- + +func T43_F() + let loops = 3 + while loops > 0 + try + if loops == 3 + Xpath 'a' . loops + throw "a" + Xpath 'b' . loops + elseif loops == 2 + Xpath 'c' . loops + throw "ab" + Xpath 'd' . loops + elseif loops == 1 + Xpath 'e' . loops + throw "abc" + Xpath 'f' . loops + endif + catch /abc/ + Xpath 'g' . loops + catch /ab/ + Xpath 'h' . loops + catch /.*/ + Xpath 'i' . loops + catch /a/ + Xpath 'j' . loops + endtry + + let loops = loops - 1 + endwhile + Xpath 'k' +endfunc + +func Test_multi_catch() + XpathINIT + call T43_F() + call assert_equal('a3i3c2h2e1g1k', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 44: Missing or empty :catch patterns {{{1 +" +" A missing or empty :catch pattern means the same as /.*/, that is, +" catches everything. To catch only empty exceptions, /^$/ must be +" used. A :catch with missing, empty, or /.*/ argument also works +" when followed by another command separated by a bar on the same +" line. :catch patterns cannot be specified between ||. But other +" pattern separators can be used instead of //. +"------------------------------------------------------------------------------- + +func T44_F() + try + try + Xpath 'a' + throw "" + catch /^$/ + Xpath 'b' + endtry + + try + Xpath 'c' + throw "" + catch /.*/ + Xpath 'd' + endtry + + try + Xpath 'e' + throw "" + catch // + Xpath 'f' + endtry + + try + Xpath 'g' + throw "" + catch + Xpath 'h' + endtry + + try + Xpath 'i' + throw "oops" + catch /^$/ + Xpath 'j' + catch /.*/ + Xpath 'k' + endtry + + try + Xpath 'l' + throw "arrgh" + catch /^$/ + Xpath 'm' + catch // + Xpath 'n' + endtry + + try + Xpath 'o' + throw "brrr" + catch /^$/ + Xpath 'p' + catch + Xpath 'q' + endtry + + try | Xpath 'r' | throw "x" | catch /.*/ | Xpath 's' | endtry + + try | Xpath 't' | throw "y" | catch // | Xpath 'u' | endtry + + while 1 + try + let caught = 0 + let v:errmsg = "" + " Extra try level: if ":catch" without arguments below raises + " a syntax error because it misinterprets the "Xpath" as a pattern, + " let it be caught by the ":catch /.*/" below. + try + try | Xpath 'v' | throw "z" | catch | Xpath 'w' | : + endtry + endtry + catch /.*/ + let caught = 1 + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + finally + if $VIMNOERRTHROW && v:errmsg != "" + call assert_report(v:errmsg) + endif + if caught || $VIMNOERRTHROW && v:errmsg != "" + Xpath 'x' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + let cologne = 4711 + try + try + Xpath 'y' + throw "throw cologne" + " Next lines catches all and throws 4711: + catch |throw cologne| + Xpath 'z' + endtry + catch /4711/ + Xpath 'A' + endtry + + try + Xpath 'B' + throw "plus" + catch +plus+ + Xpath 'C' + endtry + + Xpath 'D' + catch /.*/ + Xpath 'E' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry +endfunc + +func Test_empty_catch() + XpathINIT + call T44_F() + call assert_equal('abcdefghiklnoqrstuvwyABCD', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 45: Catching exceptions from nested :try blocks {{{1 +" +" When :try blocks are nested, an exception is caught by the innermost +" try conditional that has a matching :catch clause. +"------------------------------------------------------------------------------- + +func T45_F() + let loops = 3 + while loops > 0 + try + try + try + try + if loops == 3 + Xpath 'a' . loops + throw "a" + Xpath 'b' . loops + elseif loops == 2 + Xpath 'c' . loops + throw "ab" + Xpath 'd' . loops + elseif loops == 1 + Xpath 'e' . loops + throw "abc" + Xpath 'f' . loops + endif + catch /abc/ + Xpath 'g' . loops + endtry + catch /ab/ + Xpath 'h' . loops + endtry + catch /.*/ + Xpath 'i' . loops + endtry + catch /a/ + Xpath 'j' . loops + endtry + + let loops = loops - 1 + endwhile + Xpath 'k' +endfunc + +func Test_catch_from_nested_try() + XpathINIT + call T45_F() + call assert_equal('a3i3c2h2e1g1k', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 46: Executing :finally after a :throw in nested :try {{{1 +" +" When an exception is thrown from within nested :try blocks, the +" :finally clauses of the non-catching try conditionals should be +" executed before the matching :catch of the next surrounding :try +" gets the control. If this also has a :finally clause, it is +" executed afterwards. +"------------------------------------------------------------------------------- + +func T46_F() + let sum = 0 + + try + Xpath 'a' + try + Xpath 'b' + try + Xpath 'c' + try + Xpath 'd' + throw "ABC" + Xpath 'e' + catch /xyz/ + Xpath 'f' + finally + Xpath 'g' + if sum != 0 + Xpath 'h' + endif + let sum = sum + 1 + endtry + Xpath 'i' + catch /123/ + Xpath 'j' + catch /321/ + Xpath 'k' + finally + Xpath 'l' + if sum != 1 + Xpath 'm' + endif + let sum = sum + 2 + endtry + Xpath 'n' + finally + Xpath 'o' + if sum != 3 + Xpath 'p' + endif + let sum = sum + 4 + endtry + Xpath 'q' + catch /ABC/ + Xpath 'r' + if sum != 7 + Xpath 's' + endif + let sum = sum + 8 + finally + Xpath 't' + if sum != 15 + Xpath 'u' + endif + let sum = sum + 16 + endtry + Xpath 'v' + if sum != 31 + Xpath 'w' + endif +endfunc + +func Test_finally_after_throw() + XpathINIT + call T46_F() + call assert_equal('abcdglortv', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 47: Throwing exceptions from a :catch clause {{{1 +" +" When an exception is thrown from a :catch clause, it should not be +" caught by a :catch of the same :try conditional. After executing +" the :finally clause (if present), surrounding try conditionals +" should be checked for a matching :catch. +"------------------------------------------------------------------------------- + +func T47_F() + Xpath 'a' + try + Xpath 'b' + try + Xpath 'c' + try + Xpath 'd' + throw "x1" + Xpath 'e' + catch /x1/ + Xpath 'f' + try + Xpath 'g' + throw "x2" + Xpath 'h' + catch /x1/ + Xpath 'i' + catch /x2/ + Xpath 'j' + try + Xpath 'k' + throw "x3" + Xpath 'l' + catch /x1/ + Xpath 'm' + catch /x2/ + Xpath 'n' + finally + Xpath 'o' + endtry + Xpath 'p' + catch /x3/ + Xpath 'q' + endtry + Xpath 'r' + catch /x1/ + Xpath 's' + catch /x2/ + Xpath 't' + catch /x3/ + Xpath 'u' + finally + Xpath 'v' + endtry + Xpath 'w' + catch /x1/ + Xpath 'x' + catch /x2/ + Xpath 'y' + catch /x3/ + Xpath 'z' + endtry + Xpath 'A' + catch /.*/ + Xpath 'B' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + Xpath 'C' +endfunc + +func Test_throw_from_catch() + XpathINIT + call T47_F() + call assert_equal('abcdfgjkovzAC', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 48: Throwing exceptions from a :finally clause {{{1 +" +" When an exception is thrown from a :finally clause, it should not be +" caught by a :catch of the same :try conditional. Surrounding try +" conditionals should be checked for a matching :catch. A previously +" thrown exception is discarded. +"------------------------------------------------------------------------------- + +func T48_F() + try + + try + try + Xpath 'a' + catch /x1/ + Xpath 'b' + finally + Xpath 'c' + throw "x1" + Xpath 'd' + endtry + Xpath 'e' + catch /x1/ + Xpath 'f' + endtry + Xpath 'g' + + try + try + Xpath 'h' + throw "x2" + Xpath 'i' + catch /x2/ + Xpath 'j' + catch /x3/ + Xpath 'k' + finally + Xpath 'l' + throw "x3" + Xpath 'm' + endtry + Xpath 'n' + catch /x2/ + Xpath 'o' + catch /x3/ + Xpath 'p' + endtry + Xpath 'q' + + try + try + try + Xpath 'r' + throw "x4" + Xpath 's' + catch /x5/ + Xpath 't' + finally + Xpath 'u' + throw "x5" " discards 'x4' + Xpath 'v' + endtry + Xpath 'w' + catch /x4/ + Xpath 'x' + finally + Xpath 'y' + endtry + Xpath 'z' + catch /x5/ + Xpath 'A' + endtry + Xpath 'B' + + catch /.*/ + Xpath 'C' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + Xpath 'D' +endfunc + +func Test_throw_from_finally() + XpathINIT + call T48_F() + call assert_equal('acfghjlpqruyABD', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 51: Throwing exceptions across :execute and user commands {{{1 +" +" A :throw command may be executed under an ":execute" or from +" a user command. +"------------------------------------------------------------------------------- + +func T51_F() + command! -nargs=? THROW1 throw <args> | throw 1 + command! -nargs=? THROW2 try | throw <args> | endtry | throw 2 + command! -nargs=? THROW3 try | throw 3 | catch /3/ | throw <args> | endtry + command! -nargs=? THROW4 try | throw 4 | finally | throw <args> | endtry + + try + + try + try + Xpath 'a' + THROW1 "A" + catch /A/ + Xpath 'b' + endtry + catch /1/ + Xpath 'c' + endtry + + try + try + Xpath 'd' + THROW2 "B" + catch /B/ + Xpath 'e' + endtry + catch /2/ + Xpath 'f' + endtry + + try + try + Xpath 'g' + THROW3 "C" + catch /C/ + Xpath 'h' + endtry + catch /3/ + Xpath 'i' + endtry + + try + try + Xpath 'j' + THROW4 "D" + catch /D/ + Xpath 'k' + endtry + catch /4/ + Xpath 'l' + endtry + + try + try + Xpath 'm' + execute 'throw "E" | throw 5' + catch /E/ + Xpath 'n' + endtry + catch /5/ + Xpath 'o' + endtry + + try + try + Xpath 'p' + execute 'try | throw "F" | endtry | throw 6' + catch /F/ + Xpath 'q' + endtry + catch /6/ + Xpath 'r' + endtry + + try + try + Xpath 's' + execute'try | throw 7 | catch /7/ | throw "G" | endtry' + catch /G/ + Xpath 't' + endtry + catch /7/ + Xpath 'u' + endtry + + try + try + Xpath 'v' + execute 'try | throw 8 | finally | throw "H" | endtry' + catch /H/ + Xpath 'w' + endtry + catch /8/ + Xpath 'x' + endtry + + catch /.*/ + Xpath 'y' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + Xpath 'z' + + delcommand THROW1 + delcommand THROW2 + delcommand THROW3 + delcommand THROW4 +endfunc + +func Test_throw_across_commands() + XpathINIT + call T51_F() + call assert_equal('abdeghjkmnpqstvwz', g:Xpath) +endfunc + + + +"------------------------------------------------------------------------------- +" Test 69: :throw across :if, :elseif, :while {{{1 +" +" On an :if, :elseif, or :while command, an exception might be thrown +" during evaluation of the expression to test. The exception can be +" caught by the script. +"------------------------------------------------------------------------------- + +func T69_throw(x) + Xpath 'x' + throw a:x +endfunc + +func Test_throw_ifelsewhile() + XpathINIT + + try + try + Xpath 'a' + if 111 == T69_throw("if") + 111 + Xpath 'b' + else + Xpath 'c' + endif + Xpath 'd' + catch /^if$/ + Xpath 'e' + catch /.*/ + Xpath 'f' + call assert_report("if: " . v:exception . " in " . v:throwpoint) + endtry + + try + Xpath 'g' + if v:false + Xpath 'h' + elseif 222 == T69_throw("elseif") + 222 + Xpath 'i' + else + Xpath 'j' + endif + Xpath 'k' + catch /^elseif$/ + Xpath 'l' + catch /.*/ + Xpath 'm' + call assert_report("elseif: " . v:exception . " in " . v:throwpoint) + endtry + + try + Xpath 'n' + while 333 == T69_throw("while") + 333 + Xpath 'o' + break + endwhile + Xpath 'p' + catch /^while$/ + Xpath 'q' + catch /.*/ + Xpath 'r' + call assert_report("while: " .. v:exception .. " in " .. v:throwpoint) + endtry + catch /^0$/ " default return value + Xpath 's' + call assert_report(v:throwpoint) + catch /.*/ + call assert_report(v:exception .. " in " .. v:throwpoint) + Xpath 't' + endtry + + call assert_equal('axegxlnxq', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 70: :throw across :return or :throw {{{1 +" +" On a :return or :throw command, an exception might be thrown during +" evaluation of the expression to return or throw, respectively. The +" exception can be caught by the script. +"------------------------------------------------------------------------------- + +let T70_taken = "" + +func T70_throw(x, n) + let g:T70_taken = g:T70_taken . "T" . a:n + throw a:x +endfunc + +func T70_F(x, y, n) + let g:T70_taken = g:T70_taken . "F" . a:n + return a:x + T70_throw(a:y, a:n) +endfunc + +func T70_G(x, y, n) + let g:T70_taken = g:T70_taken . "G" . a:n + throw a:x . T70_throw(a:y, a:n) + return a:x +endfunc + +func Test_throwreturn() + XpathINIT + + try + try + Xpath 'a' + call T70_F(4711, "return", 1) + Xpath 'b' + catch /^return$/ + Xpath 'c' + catch /.*/ + Xpath 'd' + call assert_report("return: " .. v:exception .. " in " .. v:throwpoint) + endtry + + try + Xpath 'e' + let var = T70_F(4712, "return-var", 2) + Xpath 'f' + catch /^return-var$/ + Xpath 'g' + catch /.*/ + Xpath 'h' + call assert_report("return-var: " . v:exception . " in " . v:throwpoint) + finally + unlet! var + endtry + + try + Xpath 'i' + throw "except1" . T70_throw("throw1", 3) + Xpath 'j' + catch /^except1/ + Xpath 'k' + catch /^throw1$/ + Xpath 'l' + catch /.*/ + Xpath 'm' + call assert_report("throw1: " .. v:exception .. " in " .. v:throwpoint) + endtry + + try + Xpath 'n' + call T70_G("except2", "throw2", 4) + Xpath 'o' + catch /^except2/ + Xpath 'p' + catch /^throw2$/ + Xpath 'q' + catch /.*/ + Xpath 'r' + call assert_report("throw2: " .. v:exception .. " in " .. v:throwpoint) + endtry + + try + Xpath 's' + let var = T70_G("except3", "throw3", 5) + Xpath 't' + catch /^except3/ + Xpath 'u' + catch /^throw3$/ + Xpath 'v' + catch /.*/ + Xpath 'w' + call assert_report("throw3: " .. v:exception .. " in " .. v:throwpoint) + finally + unlet! var + endtry + + call assert_equal('F1T1F2T2T3G4T4G5T5', g:T70_taken) + Xpath 'x' + catch /^0$/ " default return value + Xpath 'y' + call assert_report(v:throwpoint) + catch /.*/ + Xpath 'z' + call assert_report('Caught' .. v:exception .. ' in ' .. v:throwpoint) + endtry + + call assert_equal('acegilnqsvx', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 71: :throw across :echo variants and :execute {{{1 +" +" On an :echo, :echon, :echomsg, :echoerr, or :execute command, an +" exception might be thrown during evaluation of the arguments to +" be displayed or executed as a command, respectively. Any following +" arguments are not evaluated, then. The exception can be caught by +" the script. +"------------------------------------------------------------------------------- + +let T71_taken = "" + +func T71_throw(x, n) + let g:T71_taken = g:T71_taken . "T" . a:n + throw a:x +endfunc + +func T71_F(n) + let g:T71_taken = g:T71_taken . "F" . a:n + return "F" . a:n +endfunc + +func Test_throw_echo() + XpathINIT + + try + try + Xpath 'a' + echo 'echo ' . T71_throw("echo-except", 1) . T71_F(1) + Xpath 'b' + catch /^echo-except$/ + Xpath 'c' + catch /.*/ + Xpath 'd' + call assert_report("echo: " .. v:exception .. " in " .. v:throwpoint) + endtry + + try + Xpath 'e' + echon "echon " . T71_throw("echon-except", 2) . T71_F(2) + Xpath 'f' + catch /^echon-except$/ + Xpath 'g' + catch /.*/ + Xpath 'h' + call assert_report('echon: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 'i' + echomsg "echomsg " . T71_throw("echomsg-except", 3) . T71_F(3) + Xpath 'j' + catch /^echomsg-except$/ + Xpath 'k' + catch /.*/ + Xpath 'l' + call assert_report('echomsg: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 'm' + echoerr "echoerr " . T71_throw("echoerr-except", 4) . T71_F(4) + Xpath 'n' + catch /^echoerr-except$/ + Xpath 'o' + catch /Vim/ + Xpath 'p' + catch /echoerr/ + Xpath 'q' + catch /.*/ + Xpath 'r' + call assert_report('echoerr: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 's' + execute "echo 'execute " . T71_throw("execute-except", 5) . T71_F(5) "'" + Xpath 't' + catch /^execute-except$/ + Xpath 'u' + catch /.*/ + Xpath 'v' + call assert_report('execute: ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('T1T2T3T4T5', g:T71_taken) + Xpath 'w' + catch /^0$/ " default return value + Xpath 'x' + call assert_report(v:throwpoint) + catch /.*/ + Xpath 'y' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('acegikmosuw', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 72: :throw across :let or :unlet {{{1 +" +" On a :let command, an exception might be thrown during evaluation +" of the expression to assign. On an :let or :unlet command, the +" evaluation of the name of the variable to be assigned or list or +" deleted, respectively, may throw an exception. Any following +" arguments are not evaluated, then. The exception can be caught by +" the script. +"------------------------------------------------------------------------------- + +let throwcount = 0 + +func T72_throw(x) + let g:throwcount = g:throwcount + 1 + throw a:x +endfunc + +let T72_addpath = '' + +func T72_addpath(p) + let g:T72_addpath = g:T72_addpath . a:p +endfunc + +func Test_throw_let() + XpathINIT + + try + try + let $VAR = 'old_value' + Xpath 'a' + let $VAR = 'let(' . T72_throw('var') . ')' + Xpath 'b' + catch /^var$/ + Xpath 'c' + finally + call assert_equal('old_value', $VAR) + endtry + + try + let @a = 'old_value' + Xpath 'd' + let @a = 'let(' . T72_throw('reg') . ')' + Xpath 'e' + catch /^reg$/ + try + Xpath 'f' + let @A = 'let(' . T72_throw('REG') . ')' + Xpath 'g' + catch /^REG$/ + Xpath 'h' + endtry + finally + call assert_equal('old_value', @a) + call assert_equal('old_value', @A) + endtry + + try + let saved_gpath = &g:path + let saved_lpath = &l:path + Xpath 'i' + let &path = 'let(' . T72_throw('opt') . ')' + Xpath 'j' + catch /^opt$/ + try + Xpath 'k' + let &g:path = 'let(' . T72_throw('gopt') . ')' + Xpath 'l' + catch /^gopt$/ + try + Xpath 'm' + let &l:path = 'let(' . T72_throw('lopt') . ')' + Xpath 'n' + catch /^lopt$/ + Xpath 'o' + endtry + endtry + finally + call assert_equal(saved_gpath, &g:path) + call assert_equal(saved_lpath, &l:path) + let &g:path = saved_gpath + let &l:path = saved_lpath + endtry + + unlet! var1 var2 var3 + + try + Xpath 'p' + let var1 = 'let(' . T72_throw('var1') . ')' + Xpath 'q' + catch /^var1$/ + Xpath 'r' + finally + call assert_true(!exists('var1')) + endtry + + try + let var2 = 'old_value' + Xpath 's' + let var2 = 'let(' . T72_throw('var2'). ')' + Xpath 't' + catch /^var2$/ + Xpath 'u' + finally + call assert_equal('old_value', var2) + endtry + + try + Xpath 'v' + let var{T72_throw('var3')} = 4711 + Xpath 'w' + catch /^var3$/ + Xpath 'x' + endtry + + try + call T72_addpath('T1') + let var{T72_throw('var4')} var{T72_addpath('T2')} | call T72_addpath('T3') + call T72_addpath('T4') + catch /^var4$/ + call T72_addpath('T5') + endtry + + try + call T72_addpath('T6') + unlet var{T72_throw('var5')} var{T72_addpath('T7')} + \ | call T72_addpath('T8') + call T72_addpath('T9') + catch /^var5$/ + call T72_addpath('T10') + endtry + + call assert_equal('T1T5T6T10', g:T72_addpath) + call assert_equal(11, g:throwcount) + catch /.*/ + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('acdfhikmoprsuvx', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 73: :throw across :function, :delfunction {{{1 +" +" The :function and :delfunction commands may cause an expression +" specified in braces to be evaluated. During evaluation, an +" exception might be thrown. The exception can be caught by the +" script. +"------------------------------------------------------------------------------- + +let T73_taken = '' + +func T73_throw(x, n) + let g:T73_taken = g:T73_taken . 'T' . a:n + throw a:x +endfunc + +func T73_expr(x, n) + let g:T73_taken = g:T73_taken . 'E' . a:n + if a:n % 2 == 0 + call T73_throw(a:x, a:n) + endif + return 2 - a:n % 2 +endfunc + +func Test_throw_func() + XpathINIT + + try + try + " Define function. + Xpath 'a' + function! F0() + endfunction + Xpath 'b' + function! F{T73_expr('function-def-ok', 1)}() + endfunction + Xpath 'c' + function! F{T73_expr('function-def', 2)}() + endfunction + Xpath 'd' + catch /^function-def-ok$/ + Xpath 'e' + catch /^function-def$/ + Xpath 'f' + catch /.*/ + call assert_report('def: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + " List function. + Xpath 'g' + function F0 + Xpath 'h' + function F{T73_expr('function-lst-ok', 3)} + Xpath 'i' + function F{T73_expr('function-lst', 4)} + Xpath 'j' + catch /^function-lst-ok$/ + Xpath 'k' + catch /^function-lst$/ + Xpath 'l' + catch /.*/ + call assert_report('lst: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + " Delete function + Xpath 'm' + delfunction F0 + Xpath 'n' + delfunction F{T73_expr('function-del-ok', 5)} + Xpath 'o' + delfunction F{T73_expr('function-del', 6)} + Xpath 'p' + catch /^function-del-ok$/ + Xpath 'q' + catch /^function-del$/ + Xpath 'r' + catch /.*/ + call assert_report('del: ' . v:exception . ' in ' . v:throwpoint) + endtry + call assert_equal('E1E2T2E3E4T4E5E6T6', g:T73_taken) + catch /.*/ + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('abcfghilmnor', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 74: :throw across builtin functions and commands {{{1 +" +" Some functions like exists(), searchpair() take expression +" arguments, other functions or commands like substitute() or +" :substitute cause an expression (specified in the regular +" expression) to be evaluated. During evaluation an exception +" might be thrown. The exception can be caught by the script. +"------------------------------------------------------------------------------- + +let T74_taken = "" + +func T74_throw(x, n) + let g:T74_taken = g:T74_taken . "T" . a:n + throw a:x +endfunc + +func T74_expr(x, n) + let g:T74_taken = g:T74_taken . "E" . a:n + call T74_throw(a:x . a:n, a:n) + return "EXPR" +endfunc + +func T74_skip(x, n) + let g:T74_taken = g:T74_taken . "S" . a:n . "(" . line(".") + let theline = getline(".") + if theline =~ "skip" + let g:T74_taken = g:T74_taken . "s)" + return 1 + elseif theline =~ "throw" + let g:T74_taken = g:T74_taken . "t)" + call T74_throw(a:x . a:n, a:n) + else + let g:T74_taken = g:T74_taken . ")" + return 0 + endif +endfunc + +func T74_subst(x, n) + let g:T74_taken = g:T74_taken . "U" . a:n . "(" . line(".") + let theline = getline(".") + if theline =~ "not" " T74_subst() should not be called for this line + let g:T74_taken = g:T74_taken . "n)" + call T74_throw(a:x . a:n, a:n) + elseif theline =~ "throw" + let g:T74_taken = g:T74_taken . "t)" + call T74_throw(a:x . a:n, a:n) + else + let g:T74_taken = g:T74_taken . ")" + return "replaced" + endif +endfunc + +func Test_throw_builtin_func() + XpathINIT + + try + try + Xpath 'a' + let result = exists('*{T74_expr("exists", 1)}') + Xpath 'b' + catch /^exists1$/ + Xpath 'c' + try + let result = exists('{T74_expr("exists", 2)}') + Xpath 'd' + catch /^exists2$/ + Xpath 'e' + catch /.*/ + call assert_report('exists2: ' . v:exception . ' in ' . v:throwpoint) + endtry + catch /.*/ + call assert_report('exists1: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + let file = tempname() + exec "edit" file + call append(0, [ + \ 'begin', + \ 'xx', + \ 'middle 3', + \ 'xx', + \ 'middle 5 skip', + \ 'xx', + \ 'middle 7 throw', + \ 'xx', + \ 'end']) + normal! gg + Xpath 'f' + let result = searchpair("begin", "middle", "end", '', + \ 'T74_skip("searchpair", 3)') + Xpath 'g' + let result = searchpair("begin", "middle", "end", '', + \ 'T74_skip("searchpair", 4)') + Xpath 'h' + let result = searchpair("begin", "middle", "end", '', + \ 'T74_skip("searchpair", 5)') + Xpath 'i' + catch /^searchpair[35]$/ + Xpath 'j' + catch /^searchpair4$/ + Xpath 'k' + catch /.*/ + call assert_report('searchpair: ' . v:exception . ' in ' . v:throwpoint) + finally + bwipeout! + call delete(file) + endtry + + try + let file = tempname() + exec "edit" file + call append(0, [ + \ 'subst 1', + \ 'subst 2', + \ 'not', + \ 'subst 4', + \ 'subst throw', + \ 'subst 6']) + normal! gg + Xpath 'l' + 1,2substitute/subst/\=T74_subst("substitute", 6)/ + try + Xpath 'm' + try + let v:errmsg = "" + 3substitute/subst/\=T74_subst("substitute", 7)/ + finally + if v:errmsg != "" + " If exceptions are not thrown on errors, fake the error + " exception in order to get the same execution path. + throw "faked Vim(substitute)" + endif + endtry + catch /Vim(substitute)/ " Pattern not found ('e' flag missing) + Xpath 'n' + 3substitute/subst/\=T74_subst("substitute", 8)/e + Xpath 'o' + endtry + Xpath 'p' + 4,6substitute/subst/\=T74_subst("substitute", 9)/ + Xpath 'q' + catch /^substitute[678]/ + Xpath 'r' + catch /^substitute9/ + Xpath 's' + finally + bwipeout! + call delete(file) + endtry + + try + Xpath 't' + let var = substitute("sub", "sub", '\=T74_throw("substitute()y", 10)', '') + Xpath 'u' + catch /substitute()y/ + Xpath 'v' + catch /.*/ + call assert_report('substitute()y: ' . v:exception . ' in ' + \ . v:throwpoint) + endtry + + try + Xpath 'w' + let var = substitute("not", "sub", '\=T74_throw("substitute()n", 11)', '') + Xpath 'x' + catch /substitute()n/ + Xpath 'y' + catch /.*/ + call assert_report('substitute()n: ' . v:exception . ' in ' + \ . v:throwpoint) + endtry + + call assert_equal('E1T1E2T2S3(3)S4(5s)S4(7t)T4U6(1)U6(2)U9(4)U9(5t)T9T10', + \ g:T74_taken) + + catch /.*/ + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('acefgklmnopstvwx', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 75: Errors in builtin functions. {{{1 +" +" On an error in a builtin function called inside a :try/:endtry +" region, the evaluation of the expression calling that function and +" the command containing that expression are abandoned. The error can +" be caught as an exception. +" +" A simple :call of the builtin function is a trivial case. If the +" builtin function is called in the argument list of another function, +" no further arguments are evaluated, and the other function is not +" executed. If the builtin function is called from the argument of +" a :return command, the :return command is not executed. If the +" builtin function is called from the argument of a :throw command, +" the :throw command is not executed. The evaluation of the +" expression calling the builtin function is abandoned. +"------------------------------------------------------------------------------- + +func T75_F1(arg1) + Xpath 'a' +endfunc + +func T75_F2(arg1, arg2) + Xpath 'b' +endfunc + +func T75_G() + Xpath 'c' +endfunc + +func T75_H() + Xpath 'd' +endfunc + +func T75_R() + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'e' + return append(1, "s") + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 'f' + finally + Xpath 'g' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'h' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + Xpath 'i' +endfunc + +func Test_builtin_func_error() + XpathINIT + + try + set noma " let append() fail with "E21" + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'j' + call append(1, "s") + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 'k' + finally + Xpath 'l' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'm' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'n' + call T75_F1('x' . append(1, "s")) + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 'o' + finally + Xpath 'p' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'q' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'r' + call T75_F2('x' . append(1, "s"), T75_G()) + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 's' + finally + Xpath 't' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'u' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + call T75_R() + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'v' + throw "T" . append(1, "s") + catch /E21/ + let caught = 1 + catch /^T.*/ + Xpath 'w' + catch /.*/ + Xpath 'x' + finally + Xpath 'y' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'z' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'A' + let x = "a" + let x = x . "b" . append(1, "s") . T75_H() + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 'B' + finally + Xpath 'C' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'D' + endif + call assert_equal('a', x) + break " discard error for $VIMNOERRTHROW + endtry + endwhile + catch /.*/ + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + finally + set ma& + endtry + + call assert_equal('jlmnpqrtueghivyzACD', g:Xpath) +endfunc + +" Modelines {{{1 +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker +"------------------------------------------------------------------------------- diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 0a89066a2b..4621207d19 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -393,9 +393,13 @@ func Test_addr_all() call assert_equal(len(gettabinfo()), g:a2) bwipe - command! -addr=other DoSomething echo 'nothing' + command! -addr=other DoSomething let g:a1 = <line1> | let g:a2 = <line2> DoSomething - call assert_fails('%DoSomething') + call assert_equal(line('.'), g:a1) + call assert_equal(line('.'), g:a2) + %DoSomething + call assert_equal(1, g:a1) + call assert_equal(line('$'), g:a2) delcommand DoSomething endfunc @@ -421,7 +425,7 @@ func Test_command_list() \ execute('command DoCmd')) command! -count=2 DoCmd : call assert_equal("\n Name Args Address Complete Definition" - \ .. "\n DoCmd 0 2c :", + \ .. "\n DoCmd 0 2c ? :", \ execute('command DoCmd')) " Test with various -addr= argument values. diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index 8302ccb67f..e8161f8fcb 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -103,3 +103,14 @@ func Test_list2str_str2list_latin1() call assert_equal(l, lres) call assert_equal(s, sres) endfunc + +func Test_print_overlong() + " Text with more composing characters than MB_MAXBYTES. + new + call setline(1, 'axxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') + s/x/\=nr2char(1629)/g + print + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 4edf8308e7..43907de1d9 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -919,9 +919,203 @@ func Test_if_bar_fail() call assert_equal('acdfh-acfh', g:test15_result) endfunc +"------------------------------------------------------------------------------- +" Test 16: Double :else or :elseif after :else {{{1 +" +" Multiple :elses or an :elseif after an :else are forbidden. +"------------------------------------------------------------------------------- + +func T16_F() abort + if 0 + Xpath 'a' + else + Xpath 'b' + else " aborts function + Xpath 'c' + endif + Xpath 'd' +endfunc + +func T16_G() abort + if 0 + Xpath 'a' + else + Xpath 'b' + elseif 1 " aborts function + Xpath 'c' + else + Xpath 'd' + endif + Xpath 'e' +endfunc + +func T16_H() abort + if 0 + Xpath 'a' + elseif 0 + Xpath 'b' + else + Xpath 'c' + else " aborts function + Xpath 'd' + endif + Xpath 'e' +endfunc + +func T16_I() abort + if 0 + Xpath 'a' + elseif 0 + Xpath 'b' + else + Xpath 'c' + elseif 1 " aborts function + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' +endfunc + +func Test_Multi_Else() + XpathINIT + try + call T16_F() + catch /E583:/ + Xpath 'e' + endtry + call assert_equal('be', g:Xpath) + + XpathINIT + try + call T16_G() + catch /E584:/ + Xpath 'f' + endtry + call assert_equal('bf', g:Xpath) + + XpathINIT + try + call T16_H() + catch /E583:/ + Xpath 'f' + endtry + call assert_equal('cf', g:Xpath) + + XpathINIT + try + call T16_I() + catch /E584:/ + Xpath 'g' + endtry + call assert_equal('cg', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 17: Nesting of unmatched :if or :endif inside a :while {{{1 +" +" The :while/:endwhile takes precedence in nesting over an unclosed +" :if or an unopened :endif. +"------------------------------------------------------------------------------- + +" While loops inside a function are continued on error. +func T17_F() + let loops = 3 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if (loops == 1) + Xpath 'b' . loops + continue + elseif (loops == 0) + Xpath 'c' . loops + break + elseif 1 + Xpath 'd' . loops + " endif missing! + endwhile " :endwhile after :if 1 + Xpath 'e' +endfunc + +func T17_G() + let loops = 2 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if 0 + Xpath 'b' . loops + " endif missing + endwhile " :endwhile after :if 0 +endfunc + +func T17_H() + let loops = 2 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + " if missing! + endif " :endif without :if in while + Xpath 'b' . loops + endwhile +endfunc + +" Error continuation outside a function is at the outermost :endwhile or :endif. +XpathINIT +let v:errmsg = '' +let loops = 2 +while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if 0 + Xpath 'b' . loops + " endif missing! Following :endwhile fails. +endwhile | Xpath 'c' +Xpath 'd' +call assert_match('E171:', v:errmsg) +call assert_equal('a1d', g:Xpath) + +func Test_unmatched_if_in_while() + XpathINIT + call assert_fails('call T17_F()', 'E171:') + call assert_equal('a2d2a1b1a0c0e', g:Xpath) + + XpathINIT + call assert_fails('call T17_G()', 'E171:') + call assert_equal('a1a0', g:Xpath) + + XpathINIT + call assert_fails('call T17_H()', 'E580:') + call assert_equal('a1b1a0b0', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +"------------------------------------------------------------------------------- +"------------------------------------------------------------------------------- +" Test 87 using (expr) ? funcref : funcref {{{1 +" +" Vim needs to correctly parse the funcref and even when it does +" not execute the funcref, it needs to consume the trailing () +"------------------------------------------------------------------------------- + +func Add2(x1, x2) + return a:x1 + a:x2 +endfu + +func GetStr() + return "abcdefghijklmnopqrstuvwxyp" +endfu + +func Test_funcref_with_condexpr() + call assert_equal(5, function('Add2')(2,3)) + + call assert_equal(3, 1 ? function('Add2')(1,2) : function('Add2')(2,3)) + call assert_equal(5, 0 ? function('Add2')(1,2) : function('Add2')(2,3)) + " Make sure, GetStr() still works. + call assert_equal('abcdefghijk', GetStr()[0:10]) +endfunc "------------------------------------------------------------------------------- -" Test 16: Recognizing {} in variable name. {{{1 +" Test 90: Recognizing {} in variable name. {{{1 "------------------------------------------------------------------------------- func Test_curlies() @@ -1490,5 +1684,5 @@ endfunc "------------------------------------------------------------------------------- " Modelines {{{1 -" vim: ts=8 sw=4 tw=80 fdm=marker +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker "------------------------------------------------------------------------------- diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index c630e678fd..687b1cb989 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 -' .. &columns + call assert_equal(0, winwidth(other_winnr)) %bwipe! endfunc diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 685da77b39..c6c09c80d7 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -61,6 +61,9 @@ static bool pending_mode_info_update = false; static bool pending_mode_update = false; static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE; +static bool has_mouse = false; +static int pending_has_mouse = -1; + #if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL # define UI_LOG(funname) #else @@ -220,6 +223,7 @@ void ui_refresh(void) ui_mode_info_set(); pending_mode_update = true; ui_cursor_shape(); + pending_has_mouse = -1; } int ui_pum_get_height(void) @@ -459,10 +463,69 @@ void ui_flush(void) ui_call_mode_change(cstr_as_string(full_name), ui_mode_idx); pending_mode_update = false; } + if (pending_has_mouse != has_mouse) { + (has_mouse ? ui_call_mouse_on : ui_call_mouse_off)(); + pending_has_mouse = has_mouse; + } ui_call_flush(); } + +/// Check if 'mouse' is active for the current mode +/// +/// TODO(bfredl): precompute the State -> active mapping when 'mouse' changes, +/// then this can be checked directly in ui_flush() +void ui_check_mouse(void) +{ + has_mouse = false; + // Be quick when mouse is off. + if (*p_mouse == NUL) { + return; + } + + int checkfor = MOUSE_NORMAL; // assume normal mode + if (VIsual_active) { + checkfor = MOUSE_VISUAL; + } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE) { + checkfor = MOUSE_RETURN; + } else if (State & INSERT) { + checkfor = MOUSE_INSERT; + } else if (State & CMDLINE) { + checkfor = MOUSE_COMMAND; + } else if (State == CONFIRM || State == EXTERNCMD) { + checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd" + } + + // mouse should be active if at least one of the following is true: + // - "c" is in 'mouse', or + // - 'a' is in 'mouse' and "c" is in MOUSE_A, or + // - the current buffer is a help file and 'h' is in 'mouse' and we are in a + // normal editing mode (not at hit-return message). + for (char_u *p = p_mouse; *p; p++) { + switch (*p) { + case 'a': + if (vim_strchr((char_u *)MOUSE_A, checkfor) != NULL) { + has_mouse = true; + return; + } + break; + case MOUSE_HELP: + if (checkfor != MOUSE_RETURN && curbuf->b_help) { + has_mouse = true; + return; + } + break; + default: + if (checkfor == *p) { + has_mouse = true; + return; + } + } + } +} + /// Check if current mode has changed. +/// /// May update the shape of the cursor. void ui_cursor_shape(void) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index a8b8f7aa50..da464c56dc 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -82,6 +82,8 @@ #include <string.h> #include <fcntl.h> +#include "auto/config.h" + #include "nvim/buffer.h" #include "nvim/ascii.h" #include "nvim/change.h" @@ -1255,7 +1257,8 @@ theend: /// a bit more verbose. /// Otherwise use curbuf->b_ffname to generate the undo file name. /// "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text. -void u_read_undo(char *name, char_u *hash, char_u *orig_name) +void u_read_undo(char *name, const char_u *hash, + const char_u *orig_name FUNC_ATTR_UNUSED) FUNC_ATTR_NONNULL_ARG(2) { u_header_T **uhp_table = NULL; @@ -1273,7 +1276,7 @@ void u_read_undo(char *name, char_u *hash, char_u *orig_name) // owner of the text file or equal to the current user. FileInfo file_info_orig; FileInfo file_info_undo; - if (os_fileinfo((char *)orig_name, &file_info_orig) + if (os_fileinfo((const char *)orig_name, &file_info_orig) && os_fileinfo((char *)file_name, &file_info_undo) && file_info_orig.stat.st_uid != file_info_undo.stat.st_uid && file_info_undo.stat.st_uid != getuid()) { diff --git a/src/nvim/version.c b/src/nvim/version.c index 6be2a61c6a..834d27cc84 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -16,7 +16,6 @@ #include "nvim/iconv.h" #include "nvim/version.h" #include "nvim/charset.h" -#include "nvim/macros.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" diff --git a/src/nvim/version.h b/src/nvim/version.h index c10f6fa534..4af038dd6c 100644 --- a/src/nvim/version.h +++ b/src/nvim/version.h @@ -2,6 +2,7 @@ #define NVIM_VERSION_H #include "nvim/ex_cmds_defs.h" +#include "nvim/macros.h" // defined in version.c extern char* Version; @@ -10,14 +11,20 @@ extern char* longVersion; // // Vim version number, name, etc. Patchlevel is defined in version.c. // + +// Values that change for a new release #define VIM_VERSION_MAJOR 8 #define VIM_VERSION_MINOR 0 + +// Values based on the above +#define VIM_VERSION_MAJOR_STR STR(VIM_VERSION_MAJOR) +#define VIM_VERSION_MINOR_STR STR(VIM_VERSION_MINOR) #define VIM_VERSION_100 (VIM_VERSION_MAJOR * 100 + VIM_VERSION_MINOR) // used for the runtime directory name -#define VIM_VERSION_NODOT "vim80" +#define VIM_VERSION_NODOT "vim" VIM_VERSION_MAJOR_STR VIM_VERSION_MINOR_STR // swap file compatibility (max. length is 6 chars) -#define VIM_VERSION_SHORT "8.0" +#define VIM_VERSION_SHORT VIM_VERSION_MAJOR_STR "." VIM_VERSION_MINOR_STR #ifdef INCLUDE_GENERATED_DECLARATIONS # include "version.h.generated.h" diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 900f2acd81..01f20cf29a 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -208,6 +208,7 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() // Size in bytes of the hash used in the undo file. #define UNDO_HASH_SIZE 32 +#define CLEAR_POINTER(ptr) memset((ptr), 0, sizeof(*(ptr))) // defines to avoid typecasts from (char_u *) to (char *) and back // (vim_strchr() is now in strings.c) diff --git a/src/nvim/window.c b/src/nvim/window.c index 72ee400e40..00f49724b6 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -641,7 +641,7 @@ void win_set_minimal_style(win_T *wp) wp->w_p_scl = (char_u *)xstrdup("auto"); } - // foldcolumn: use 'auto' + // foldcolumn: use '0' if (wp->w_p_fdc[0] != '0') { xfree(wp->w_p_fdc); wp->w_p_fdc = (char_u *)xstrdup("0"); @@ -700,9 +700,10 @@ int win_fdccol_count(win_T *wp) const char *fdc = (const char *)wp->w_p_fdc; // auto:<NUM> - if (strncmp(fdc, "auto:", 5) == 0) { + if (strncmp(fdc, "auto", 4) == 0) { + const int fdccol = fdc[4] == ':' ? fdc[5] - '0' : 1; int needed_fdccols = getDeepestNesting(wp); - return MIN(fdc[5] - '0', needed_fdccols); + return MIN(fdccol, needed_fdccols); } else { return fdc[0] - '0'; } @@ -1636,6 +1637,19 @@ bool win_valid(const win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return false; } +// Find window "handle" in the current tab page. +// Return NULL if not found. +win_T *win_find_by_handle(handle_T handle) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->handle == handle) { + return wp; + } + } + return NULL; +} + /// Check if "win" is a pointer to an existing window in any tabpage. /// /// @param win window to check @@ -3787,32 +3801,35 @@ void free_tabpage(tabpage_T *tp) /// @return Was the new tabpage created successfully? FAIL or OK. int win_new_tabpage(int after, char_u *filename) { - tabpage_T *tp = curtab; + tabpage_T *old_curtab = curtab; tabpage_T *newtp; int n; newtp = alloc_tabpage(); - /* Remember the current windows in this Tab page. */ - if (leave_tabpage(curbuf, TRUE) == FAIL) { + // Remember the current windows in this Tab page. + if (leave_tabpage(curbuf, true) == FAIL) { xfree(newtp); return FAIL; } - newtp->tp_localdir = tp->tp_localdir ? vim_strsave(tp->tp_localdir) : NULL; + newtp->tp_localdir = old_curtab->tp_localdir + ? vim_strsave(old_curtab->tp_localdir) : NULL; curtab = newtp; - /* Create a new empty window. */ - if (win_alloc_firstwin(tp->tp_curwin) == OK) { - /* Make the new Tab page the new topframe. */ + // Create a new empty window. + if (win_alloc_firstwin(old_curtab->tp_curwin) == OK) { + // Make the new Tab page the new topframe. if (after == 1) { - /* New tab page becomes the first one. */ + // New tab page becomes the first one. newtp->tp_next = first_tabpage; first_tabpage = newtp; } else { + tabpage_T *tp = old_curtab; + if (after > 0) { - /* Put new tab page before tab page "after". */ + // Put new tab page before tab page "after". n = 2; for (tp = first_tabpage; tp->tp_next != NULL && n < after; tp = tp->tp_next) @@ -3826,13 +3843,13 @@ int win_new_tabpage(int after, char_u *filename) win_comp_scroll(curwin); newtp->tp_topframe = topframe; - last_status(FALSE); + last_status(false); redraw_all_later(NOT_VALID); - tabpage_check_windows(tp); + tabpage_check_windows(old_curtab); - lastused_tabpage = tp; + lastused_tabpage = old_curtab; apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); @@ -3842,8 +3859,8 @@ int win_new_tabpage(int after, char_u *filename) return OK; } - /* Failed, get back the previous Tab page */ - enter_tabpage(curtab, curbuf, TRUE, TRUE); + // Failed, get back the previous Tab page + enter_tabpage(curtab, curbuf, true, true); return FAIL; } @@ -4523,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) { @@ -4996,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; @@ -5004,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++]; @@ -5329,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; @@ -5856,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; @@ -5915,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/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 8ed642b43e..fb8ed6a9d7 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -392,6 +392,129 @@ describe('api/buf', function() end) end) + describe('nvim_buf_get_lines, nvim_buf_set_text', function() + local get_lines, set_text = curbufmeths.get_lines, curbufmeths.set_text + + it('works', function() + insert([[ + hello foo! + text + ]]) + + eq({'hello foo!'}, get_lines(0, 1, true)) + + + -- can replace a single word + set_text(0, 6, 0, 9, {'world'}) + eq({'hello world!', 'text'}, get_lines(0, 2, true)) + + -- can insert text + set_text(0, 0, 0, 0, {'well '}) + eq({'well hello world!', 'text'}, get_lines(0, 2, true)) + + -- can delete text + set_text(0, 0, 0, 5, {''}) + eq({'hello world!', 'text'}, get_lines(0, 2, true)) + + -- can replace with multiple lines + set_text(0, 6, 0, 11, {'foo', 'wo', 'more'}) + eq({'hello foo', 'wo', 'more!', 'text'}, get_lines(0, 4, true)) + + -- will join multiple lines if needed + set_text(0, 6, 3, 4, {'bar'}) + eq({'hello bar'}, get_lines(0, 1, true)) + end) + + it('works with undo', function() + insert([[ + hello world! + foo bar + ]]) + + -- setting text + set_text(0, 0, 0, 0, {'well '}) + feed('u') + eq({'hello world!'}, get_lines(0, 1, true)) + + -- deleting text + set_text(0, 0, 0, 6, {''}) + feed('u') + eq({'hello world!'}, get_lines(0, 1, true)) + + -- inserting newlines + set_text(0, 0, 0, 0, {'hello', 'mr '}) + feed('u') + eq({'hello world!'}, get_lines(0, 1, true)) + + -- deleting newlines + set_text(0, 0, 1, 4, {'hello'}) + feed('u') + eq({'hello world!'}, get_lines(0, 1, true)) + end) + + it('updates the cursor position', function() + insert([[ + hello world! + ]]) + + -- position the cursor on `!` + curwin('set_cursor', {1, 11}) + -- replace 'world' with 'foo' + set_text(0, 6, 0, 11, {'foo'}) + eq('hello foo!', curbuf_depr('get_line', 0)) + -- cursor should be moved left by two columns (replacement is shorter by 2 chars) + eq({1, 9}, curwin('get_cursor')) + end) + + it('can handle NULs', function() + set_text(0, 0, 0, 0, {'ab\0cd'}) + eq('ab\0cd', curbuf_depr('get_line', 0)) + end) + + it('adjusts extmarks', function() + local ns = request('nvim_create_namespace', "my-fancy-plugin") + insert([[ + foo bar + baz + ]]) + local id1 = curbufmeths.set_extmark(ns, 0, 1, {}) + local id2 = curbufmeths.set_extmark(ns, 0, 7, {}) + local id3 = curbufmeths.set_extmark(ns, 1, 1, {}) + set_text(0, 4, 0, 7, {"q"}) + + eq({'foo q', 'baz'}, get_lines(0, 2, true)) + -- mark before replacement point is unaffected + eq({0, 1}, curbufmeths.get_extmark_by_id(ns, id1, {})) + -- mark gets shifted back because the replacement was shorter + eq({0, 5}, curbufmeths.get_extmark_by_id(ns, id2, {})) + -- mark on the next line is unaffected + eq({1, 1}, curbufmeths.get_extmark_by_id(ns, id3, {})) + + -- replacing the text spanning two lines will adjust the mark on the next line + set_text(0, 3, 1, 3, {"qux"}) + eq({'fooqux', ''}, get_lines(0, 2, true)) + eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id3, {})) + -- but mark before replacement point is still unaffected + eq({0, 1}, curbufmeths.get_extmark_by_id(ns, id1, {})) + -- and the mark in the middle was shifted to the end of the insertion + eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id2, {})) + + -- marks should be put back into the same place after undoing + set_text(0, 0, 0, 2, {''}) + feed('u') + eq({0, 1}, curbufmeths.get_extmark_by_id(ns, id1, {})) + eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id2, {})) + eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id3, {})) + + -- marks should be shifted over by the correct number of bytes for multibyte + -- chars + set_text(0, 0, 0, 0, {'Ø'}) + eq({0, 3}, curbufmeths.get_extmark_by_id(ns, id1, {})) + eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id2, {})) + eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id3, {})) + end) + end) + describe('nvim_buf_get_offset', function() local get_offset = curbufmeths.get_offset it('works', function() diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index d8a9c3b411..4194945645 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -809,4 +809,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() command('normal lhs') eq({'rhs'}, bufmeths.get_lines(0, 0, 1, 1)) end) + + it("does not crash when setting keymap in a non-existing buffer #13541", function() + pcall_err(bufmeths.set_keymap, 100, '', 'lsh', 'irhs<Esc>', {}) + helpers.assert_alive() + end) end) 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/api/vim_spec.lua b/test/functional/api/vim_spec.lua index eb5fd7eca7..30128e9c40 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -972,6 +972,12 @@ describe('API', function() nvim("input", "gu") eq({mode='no', blocking=false}, nvim("get_mode")) end) + + it("at '-- More --' prompt returns blocking=true #11899", function() + command('set more') + feed(':digraphs<cr>') + eq({mode='rm', blocking=true}, nvim("get_mode")) + end) end) describe('RPC (K_EVENT) #6166', function() @@ -1921,4 +1927,79 @@ describe('API', function() eq({}, meths.get_runtime_file("foobarlang/", true)) end) end) + + describe('nvim_get_all_options_info', function() + it('should have key value pairs of option names', function() + local options_info = meths.get_all_options_info() + neq(nil, options_info.listchars) + neq(nil, options_info.tabstop) + + eq(meths.get_option_info'winhighlight', options_info.winhighlight) + end) + end) + + describe('nvim_get_option_info', function() + it('should error for unknown options', function() + eq("no such option: 'bogus'", pcall_err(meths.get_option_info, 'bogus')) + end) + + it('should return the same options for short and long name', function() + eq(meths.get_option_info'winhl', meths.get_option_info'winhighlight') + end) + + it('should have information about window options', function() + eq({ + commalist = false; + default = ""; + flaglist = false; + global_local = false; + last_set_chan = 0; + last_set_linenr = 0; + last_set_sid = 0; + name = "winhighlight"; + scope = "win"; + shortname = "winhl"; + type = "string"; + was_set = false; + }, meths.get_option_info'winhl') + end) + + it('should have information about buffer options', function() + eq({ + commalist = false, + default = "", + flaglist = false, + global_local = false, + last_set_chan = 0, + last_set_linenr = 0, + last_set_sid = 0, + name = "filetype", + scope = "buf", + shortname = "ft", + type = "string", + was_set = false + }, meths.get_option_info'filetype') + end) + + it('should have information about global options', function() + -- precondition: the option was changed from its default + -- in test setup. + eq(false, meths.get_option'showcmd') + + eq({ + commalist = false, + default = true, + flaglist = false, + global_local = false, + last_set_chan = 0, + last_set_linenr = 0, + last_set_sid = -2, + name = "showcmd", + scope = "global", + shortname = "sc", + type = "boolean", + was_set = true + }, meths.get_option_info'showcmd') + end) + end) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index ff0fdbea45..d5f03db03a 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -428,9 +428,9 @@ end) describe('clean', function() clear() - ok(string.match(meths.get_option('runtimepath'), funcs.stdpath('config')) ~= nil) + ok(string.find(meths.get_option('runtimepath'), funcs.stdpath('config'), 1, true) ~= nil) clear('--clean') - ok(string.match(meths.get_option('runtimepath'), funcs.stdpath('config')) == nil) + ok(string.find(meths.get_option('runtimepath'), funcs.stdpath('config'), 1, true) == nil) end) describe('user config init', function() diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua index ed110efeb4..7d09a652ba 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/eval/api_functions_spec.lua @@ -47,6 +47,10 @@ describe('eval-API', function() eq('Vim(call):E5555: API call: Invalid buffer id: 17', err) end) + it('cannot change texts if textlocked', function() + command("autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])") + eq('Vim(call):E5555: API call: E523: Not allowed here', pcall_err(command, "normal! yy")) + end) it("use buffer numbers and windows ids as handles", function() local screen = Screen.new(40, 8) diff --git a/test/functional/eval/environ_spec.lua b/test/functional/eval/environ_spec.lua index 54d2dc960b..9e19568249 100644 --- a/test/functional/eval/environ_spec.lua +++ b/test/functional/eval/environ_spec.lua @@ -3,6 +3,11 @@ local clear = helpers.clear local eq = helpers.eq local environ = helpers.funcs.environ local exists = helpers.funcs.exists +local system = helpers.funcs.system +local nvim_prog = helpers.nvim_prog +local command = helpers.command +local eval = helpers.eval +local setenv = helpers.funcs.setenv describe('environment variables', function() it('environ() handles empty env variable', function() @@ -17,3 +22,59 @@ describe('environment variables', function() eq(0, exists('$DOES_NOT_EXIST')) end) end) + +describe('empty $HOME', function() + local original_home = os.getenv('HOME') + + -- recover $HOME after each test + after_each(function() + if original_home ~= nil then + setenv('HOME', original_home) + end + os.remove('test_empty_home') + os.remove('./~') + end) + + local function tilde_in_cwd() + -- get files in cwd + command("let test_empty_home_cwd_files = split(globpath('.', '*'), '\n')") + -- get the index of the file named '~' + command('let test_empty_home_tilde_index = index(test_empty_home_cwd_files, "./~")') + return eval('test_empty_home_tilde_index') ~= -1 + end + + local function write_and_test_tilde() + system({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', + '-c', 'write test_empty_home', '+q'}) + eq(false, tilde_in_cwd()) + end + + it("'~' folder not created in cwd if $HOME and related env not defined", function() + command("unlet $HOME") + write_and_test_tilde() + + command("let $HOMEDRIVE='C:'") + command("let $USERPROFILE='C:\\'") + write_and_test_tilde() + + command("unlet $HOMEDRIVE") + write_and_test_tilde() + + command("unlet $USERPROFILE") + write_and_test_tilde() + + command("let $HOME='%USERPROFILE%'") + command("let $USERPROFILE='C:\\'") + write_and_test_tilde() + end) + + it("'~' folder not created in cwd if writing a file with invalid $HOME", function() + setenv('HOME', '/path/does/not/exist') + write_and_test_tilde() + end) + + it("'~' folder not created in cwd if writing a file with $HOME=''", function() + command("let $HOME=''") + write_and_test_tilde() + end) +end) diff --git a/test/functional/eval/executable_spec.lua b/test/functional/eval/executable_spec.lua index a1cf056907..28aefb72e5 100644 --- a/test/functional/eval/executable_spec.lua +++ b/test/functional/eval/executable_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local eq, clear, call, iswin, write_file, command = helpers.eq, helpers.clear, helpers.call, helpers.iswin, helpers.write_file, helpers.command +local exc_exec = helpers.exc_exec local eval = helpers.eval describe('executable()', function() @@ -10,6 +11,20 @@ describe('executable()', function() it('returns 1 for commands in $PATH', function() local exe = iswin() and 'ping' or 'ls' eq(1, call('executable', exe)) + command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') + eq(1, call('executable', 'null')) + eq(1, call('executable', 'true')) + eq(1, call('executable', 'false')) + end) + + it('fails for invalid values', function() + for _, input in ipairs({'""', 'v:null', 'v:true', 'v:false', '{}', '[]'}) do + eq('Vim(call):E928: String required', exc_exec('call executable('..input..')')) + end + command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') + for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do + eq('Vim(call):E928: String required', exc_exec('call executable('..input..')')) + end end) it('returns 0 for non-existent files', function() diff --git a/test/functional/eval/exepath_spec.lua b/test/functional/eval/exepath_spec.lua index 10a11aeacc..08d2c59af8 100644 --- a/test/functional/eval/exepath_spec.lua +++ b/test/functional/eval/exepath_spec.lua @@ -1,14 +1,40 @@ local helpers = require('test.functional.helpers')(after_each) local eq, clear, call, iswin = helpers.eq, helpers.clear, helpers.call, helpers.iswin +local command = helpers.command +local exc_exec = helpers.exc_exec +local matches = helpers.matches -describe('exepath() (Windows)', function() - if not iswin() then return end -- N/A for Unix. +describe('exepath()', function() + before_each(clear) - it('append extension if omitted', function() - local filename = 'cmd' - local pathext = '.exe' - clear({env={PATHEXT=pathext}}) - eq(call('exepath', filename..pathext), call('exepath', filename)) + it('returns 1 for commands in $PATH', function() + local exe = iswin() and 'ping' or 'ls' + local ext_pat = iswin() and '%.EXE$' or '$' + matches(exe .. ext_pat, call('exepath', exe)) + command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') + ext_pat = iswin() and '%.CMD$' or '$' + matches('null' .. ext_pat, call('exepath', 'null')) + matches('true' .. ext_pat, call('exepath', 'true')) + matches('false' .. ext_pat, call('exepath', 'false')) end) + + it('fails for invalid values', function() + for _, input in ipairs({'""', 'v:null', 'v:true', 'v:false', '{}', '[]'}) do + eq('Vim(call):E928: String required', exc_exec('call exepath('..input..')')) + end + command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') + for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do + eq('Vim(call):E928: String required', exc_exec('call exepath('..input..')')) + end + end) + + if iswin() then + it('append extension if omitted', function() + local filename = 'cmd' + local pathext = '.exe' + clear({env={PATHEXT=pathext}}) + eq(call('exepath', filename..pathext), call('exepath', filename)) + end) + end end) diff --git a/test/functional/eval/function_spec.lua b/test/functional/eval/function_spec.lua index 776e760aaf..ce8850fcc2 100644 --- a/test/functional/eval/function_spec.lua +++ b/test/functional/eval/function_spec.lua @@ -2,7 +2,10 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local eq = helpers.eq +local matches = helpers.matches local exc_exec = helpers.exc_exec +local iswin = helpers.iswin +local eval = helpers.eval describe('Up to MAX_FUNC_ARGS arguments are handled by', function() local max_func_args = 20 -- from eval.h @@ -27,3 +30,8 @@ describe('Up to MAX_FUNC_ARGS arguments are handled by', function() eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) end) end) + +it('windowsversion()', function() + clear() + matches(iswin() and '^%d+%.%d+$' or '^$', eval('windowsversion()')) +end) diff --git a/test/functional/fixtures/bin/false b/test/functional/fixtures/bin/false new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/bin/false diff --git a/test/functional/fixtures/bin/false.cmd b/test/functional/fixtures/bin/false.cmd new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/bin/false.cmd diff --git a/test/functional/fixtures/bin/null b/test/functional/fixtures/bin/null new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/bin/null diff --git a/test/functional/fixtures/bin/null.cmd b/test/functional/fixtures/bin/null.cmd new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/bin/null.cmd diff --git a/test/functional/fixtures/bin/true b/test/functional/fixtures/bin/true new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/bin/true diff --git a/test/functional/fixtures/bin/true.cmd b/test/functional/fixtures/bin/true.cmd new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/bin/true.cmd diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index a30eb748d0..252db88b6b 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -109,6 +109,23 @@ function tests.basic_init() } end +function tests.check_workspace_configuration() + skeleton { + on_init = function(_params) + return { capabilities = {} } + end; + body = function() + notify('start') + notify('workspace/configuration', { items = { + { section = "testSetting1" }; + { section = "testSetting2" }; + } }) + expect_notification('workspace/configuration', { true; vim.NIL}) + notify('shutdown') + end; + } +end + function tests.basic_check_capabilities() skeleton { on_init = function(params) diff --git a/test/functional/fixtures/lua/syntax_error.lua b/test/functional/fixtures/lua/syntax_error.lua new file mode 100644 index 0000000000..8c0cf026ee --- /dev/null +++ b/test/functional/fixtures/lua/syntax_error.lua @@ -0,0 +1 @@ +- <= the syntax error diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 0829560b9c..4acb1a7d8d 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -725,6 +725,19 @@ function module.pending_win32(pending_fn) end end +function module.pending_c_parser(pending_fn) + local status, msg = unpack(module.exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]])) + if not status then + if module.isCI() then + error("treesitter C parser not found, required on CI: " .. msg) + else + pending_fn 'no C parser, skipping' + return true + end + end + return false +end + -- Calls pending() and returns `true` if the system is too slow to -- run fragile or expensive tests. Else returns `false`. function module.skip_fragile(pending_fn, cond) diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua index d48b8882af..515d6d91b8 100644 --- a/test/functional/legacy/assert_spec.lua +++ b/test/functional/legacy/assert_spec.lua @@ -242,9 +242,9 @@ describe('assert function:', function() -- assert_fails({cmd}, [, {error}]) describe('assert_fails', function() it('should change v:errors when error does not match v:errmsg', function() - eq(1, eval([[assert_fails('xxx', {})]])) - command([[call assert_match("Expected {} but got 'E731:", v:errors[0])]]) - expected_errors({"Expected {} but got 'E731: using Dictionary as a String'"}) + eq(1, eval([[assert_fails('xxx', 'E12345')]])) + command([[call assert_match("Expected 'E12345' but got 'E492:", v:errors[0])]]) + expected_errors({"Expected 'E12345' but got 'E492: Not an editor command: xxx': xxx"}) end) it('should not change v:errors when cmd errors', function() @@ -258,9 +258,9 @@ describe('assert function:', function() end) it('can specify and get a message about what failed', function() - eq(1, eval([[assert_fails('xxx', {}, 'stupid')]])) - command([[call assert_match("stupid: Expected {} but got 'E731:", v:errors[0])]]) - expected_errors({"stupid: Expected {} but got 'E731: using Dictionary as a String'"}) + eq(1, eval([[assert_fails('xxx', 'E9876', 'stupid')]])) + command([[call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0])]]) + expected_errors({"stupid: Expected 'E9876' but got 'E492: Not an editor command: xxx': stupid"}) end) it('can specify and get a message even when cmd succeeds', function() diff --git a/test/functional/legacy/backspace_opt_spec.lua b/test/functional/legacy/backspace_opt_spec.lua deleted file mode 100644 index 90bc6f74f0..0000000000 --- a/test/functional/legacy/backspace_opt_spec.lua +++ /dev/null @@ -1,67 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local call, clear = helpers.call, helpers.clear -local source, eq, nvim = helpers.source, helpers.eq, helpers.meths - -describe("test 'backspace' settings", function() - before_each(function() - clear() - - source([[ - func Exec(expr) - let str='' - try - exec a:expr - catch /.*/ - let str=v:exception - endtry - return str - endfunc - - func Test_backspace_option() - set backspace= - call assert_equal('', &backspace) - set backspace=indent - call assert_equal('indent', &backspace) - set backspace=eol - call assert_equal('eol', &backspace) - set backspace=start - call assert_equal('start', &backspace) - " Add the value - set backspace= - set backspace=indent - call assert_equal('indent', &backspace) - set backspace+=eol - call assert_equal('indent,eol', &backspace) - set backspace+=start - call assert_equal('indent,eol,start', &backspace) - " Delete the value - set backspace-=indent - call assert_equal('eol,start', &backspace) - set backspace-=start - call assert_equal('eol', &backspace) - set backspace-=eol - call assert_equal('', &backspace) - " Check the error - call assert_equal(0, match(Exec('set backspace=ABC'), '.*E474')) - call assert_equal(0, match(Exec('set backspace+=def'), '.*E474')) - " NOTE: Vim doesn't check following error... - "call assert_equal(0, match(Exec('set backspace-=ghi'), '.*E474')) - - " Check backwards compatibility with version 5.4 and earlier - set backspace=0 - call assert_equal('0', &backspace) - set backspace=1 - call assert_equal('1', &backspace) - set backspace=2 - call assert_equal('2', &backspace) - call assert_false(match(Exec('set backspace=3'), '.*E474')) - call assert_false(match(Exec('set backspace=10'), '.*E474')) - endfunc - ]]) - end) - - it('works', function() - call('Test_backspace_option') - eq({}, nvim.get_vvar('errors')) - end) -end) diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index fb0bacc2d2..5f7bbd887f 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -10,6 +10,29 @@ local source = helpers.source local poke_eventloop = helpers.poke_eventloop local uname = helpers.uname local load_adjust = helpers.load_adjust +local isCI = helpers.isCI + +local function isasan() + local version = eval('execute("version")') + return version:match('-fsanitize=[a-z,]*address') +end + +clear() +if isasan() then + pending('ASAN build is difficult to estimate memory usage', function() end) + return +elseif iswin() then + if isCI('github') then + pending('Windows runners in Github Actions do not have a stable environment to estimate memory usage', function() end) + return + elseif eval("executable('wmic')") == 0 then + pending('missing "wmic" command', function() end) + return + end +elseif eval("executable('ps')") == 0 then + pending('missing "ps" command', function() end) + return +end local monitor_memory_usage = { memory_usage = function(self) @@ -71,11 +94,6 @@ describe('memory usage', function() end end - local function isasan() - local version = eval('execute("version")') - return version:match('-fsanitize=[a-z,]*address') - end - before_each(clear) --[[ @@ -83,15 +101,6 @@ describe('memory usage', function() just after it finishes. ]]-- it('function capture vargs', function() - if isasan() then - pending('ASAN build is difficult to estimate memory usage') - end - if iswin() and eval("executable('wmic')") == 0 then - pending('missing "wmic" command') - elseif eval("executable('ps')") == 0 then - pending('missing "ps" command') - end - local pid = eval('getpid()') local before = monitor_memory_usage(pid) source([[ @@ -125,15 +134,6 @@ describe('memory usage', function() increase so much even when rerun Xtest.vim since system memory caches. ]]-- it('function capture lvars', function() - if isasan() then - pending('ASAN build is difficult to estimate memory usage') - end - if iswin() and eval("executable('wmic')") == 0 then - pending('missing "wmic" command') - elseif eval("executable('ps')") == 0 then - pending('missing "ps" command') - end - local pid = eval('getpid()') local before = monitor_memory_usage(pid) local fname = source([[ diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 7e4de7c39a..67dc5f5a16 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -25,14 +25,14 @@ local function attach_buffer(evname) local evname = ... local events = {} - function test_register(bufnr, id, changedtick, utf_sizes) + function test_register(bufnr, id, changedtick, utf_sizes, preview) local function callback(...) table.insert(events, {id, ...}) if test_unreg == id then return true end end - local opts = {[evname]=callback, on_detach=callback, utf_sizes=utf_sizes} + local opts = {[evname]=callback, on_detach=callback, utf_sizes=utf_sizes, preview=preview} if changedtick then opts.on_changedtick = callback end @@ -288,9 +288,10 @@ describe('lua: nvim_buf_attach on_bytes', function() -- TODO: while we are brewing the real strong coffe, -- verify should check buf_get_offset after every check_events if verify then - meths.buf_get_offset(0, meths.buf_line_count(0)) + local len = meths.buf_get_offset(0, meths.buf_line_count(0)) + eq(len == -1 and 1 or len, string.len(shadowbytes)) end - exec_lua("return test_register(...)", 0, "test1",false, nil) + exec_lua("return test_register(...)", 0, "test1", false, false, true) meths.buf_get_changedtick(0) local verify_name = "test1" @@ -493,6 +494,87 @@ describe('lua: nvim_buf_attach on_bytes', function() } end) + + it('inccomand=nosplit and substitute', function() + if verify then pending("Verification can't be done when previewing") end + + local check_events = setup_eventcheck(verify, {"abcde"}) + meths.set_option('inccommand', 'nosplit') + + feed ':%s/bcd/' + check_events { + { "test1", "bytes", 1, 3, 0, 1, 1, 0, 3, 3, 0, 0, 0 }; + } + + feed 'a' + check_events { + { "test1", "bytes", 1, 3, 0, 1, 1, 0, 3, 3, 0, 1, 1 }; + } + end) + + it('nvim_buf_set_text insert', function() + local check_events = setup_eventcheck(verify, {"bastext"}) + meths.buf_set_text(0, 0, 3, 0, 3, {"fiol","kontra"}) + check_events { + { "test1", "bytes", 1, 3, 0, 3, 3, 0, 0, 0, 1, 6, 11 }; + } + + meths.buf_set_text(0, 1, 6, 1, 6, {"punkt","syntgitarr","övnings"}) + check_events { + { "test1", "bytes", 1, 4, 1, 6, 14, 0, 0, 0, 2, 8, 25 }; + } + + eq({ "basfiol", "kontrapunkt", "syntgitarr", "övningstext" }, + meths.buf_get_lines(0, 0, -1, true)) + end) + + it('nvim_buf_set_text replace', function() + local check_events = setup_eventcheck(verify, origlines) + + meths.buf_set_text(0, 2, 3, 2, 8, {"very text"}) + check_events { + { "test1", "bytes", 1, 3, 2, 3, 35, 0, 5, 5, 0, 9, 9 }; + } + + meths.buf_set_text(0, 3, 5, 3, 7, {" splitty","line "}) + check_events { + { "test1", "bytes", 1, 4, 3, 5, 57, 0, 2, 2, 1, 5, 14 }; + } + + meths.buf_set_text(0, 0, 8, 1, 2, {"JOINY"}) + check_events { + { "test1", "bytes", 1, 5, 0, 8, 8, 1, 2, 10, 0, 5, 5 }; + } + + meths.buf_set_text(0, 4, 0, 6, 0, {"was 5,6",""}) + check_events { + { "test1", "bytes", 1, 6, 4, 0, 75, 2, 0, 32, 1, 0, 8 }; + } + + eq({ "originalJOINYiginal line 2", "orivery text line 3", "origi splitty", + "line l line 4", "was 5,6", " indented line" }, + meths.buf_get_lines(0, 0, -1, true)) + + end) + + it('nvim_buf_set_text delete', function() + local check_events = setup_eventcheck(verify, origlines) + + -- really {""} but accepts {} as a shorthand + meths.buf_set_text(0, 0, 0, 1, 0, {}) + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 1, 0, 16, 0, 0, 0 }; + } + + -- TODO(bfredl): this works but is not as convenient as set_lines + meths.buf_set_text(0, 4, 15, 5, 17, {""}) + check_events { + { "test1", "bytes", 1, 4, 4, 15, 79, 1, 17, 18, 0, 0, 0 }; + } + eq({ "original line 2", "original line 3", "original line 4", + "original line 5", "original line 6" }, + meths.buf_get_lines(0, 0, -1, true)) + end) end describe('(with verify) handles', function() diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua deleted file mode 100644 index 65dc1b3e03..0000000000 --- a/test/functional/lua/treesitter_spec.lua +++ /dev/null @@ -1,922 +0,0 @@ --- Test suite for testing interactions with API bindings -local helpers = require('test.functional.helpers')(after_each) -local Screen = require('test.functional.ui.screen') - -local clear = helpers.clear -local eq = helpers.eq -local insert = helpers.insert -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local pcall_err = helpers.pcall_err -local matches = helpers.matches - -before_each(clear) - -describe('treesitter API', function() - -- error tests not requiring a parser library - it('handles missing language', function() - eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", - pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")) - - -- actual message depends on platform - matches("Error executing lua: Failed to load parser: uv_dlopen: .+", - pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')")) - - eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", - pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')")) - end) -end) - -describe('treesitter API with C parser', function() - local function check_parser() - local status, msg = unpack(exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]])) - if not status then - if helpers.isCI() then - error("treesitter C parser not found, required on CI: " .. msg) - else - pending('no C parser, skipping') - end - end - return status - end - - it('parses buffer', function() - if helpers.pending_win32(pending) or not check_parser() then return end - - insert([[ - int main() { - int x = 3; - }]]) - - exec_lua([[ - parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse()[1] - root = tree:root() - lang = vim.treesitter.inspect_language('c') - ]]) - - eq("<tree>", exec_lua("return tostring(tree)")) - eq("<node translation_unit>", exec_lua("return tostring(root)")) - eq({0,0,3,0}, exec_lua("return {root:range()}")) - - eq(1, exec_lua("return root:child_count()")) - exec_lua("child = root:child(0)") - eq("<node function_definition>", exec_lua("return tostring(child)")) - eq({0,0,2,1}, exec_lua("return {child:range()}")) - - eq("function_definition", exec_lua("return child:type()")) - eq(true, exec_lua("return child:named()")) - eq("number", type(exec_lua("return child:symbol()"))) - eq({'function_definition', true}, exec_lua("return lang.symbols[child:symbol()]")) - - exec_lua("anon = root:descendant_for_range(0,8,0,9)") - eq("(", exec_lua("return anon:type()")) - eq(false, exec_lua("return anon:named()")) - eq("number", type(exec_lua("return anon:symbol()"))) - eq({'(', false}, exec_lua("return lang.symbols[anon:symbol()]")) - - exec_lua("descendant = root:descendant_for_range(1,2,1,12)") - eq("<node declaration>", exec_lua("return tostring(descendant)")) - eq({1,2,1,12}, exec_lua("return {descendant:range()}")) - eq("(declaration type: (primitive_type) declarator: (init_declarator declarator: (identifier) value: (number_literal)))", exec_lua("return descendant:sexpr()")) - - feed("2G7|ay") - exec_lua([[ - tree2 = parser:parse()[1] - root2 = tree2:root() - descendant2 = root2:descendant_for_range(1,2,1,13) - ]]) - eq(false, exec_lua("return tree2 == tree1")) - eq(false, exec_lua("return root2 == root")) - eq("<node declaration>", exec_lua("return tostring(descendant2)")) - eq({1,2,1,13}, exec_lua("return {descendant2:range()}")) - - eq(true, exec_lua("return child == child")) - -- separate lua object, but represents same node - eq(true, exec_lua("return child == root:child(0)")) - eq(false, exec_lua("return child == descendant2")) - eq(false, exec_lua("return child == nil")) - eq(false, exec_lua("return child == tree")) - - eq("string", exec_lua("return type(child:id())")) - eq(true, exec_lua("return child:id() == child:id()")) - -- separate lua object, but represents same node - eq(true, exec_lua("return child:id() == root:child(0):id()")) - eq(false, exec_lua("return child:id() == descendant2:id()")) - eq(false, exec_lua("return child:id() == nil")) - eq(false, exec_lua("return child:id() == tree")) - - -- unchanged buffer: return the same tree - eq(true, exec_lua("return parser:parse()[1] == tree2")) - end) - - local test_text = [[ -void ui_refresh(void) -{ - int width = INT_MAX, height = INT_MAX; - bool ext_widgets[kUIExtCount]; - for (UIExtension i = 0; (int)i < kUIExtCount; i++) { - ext_widgets[i] = true; - } - - bool inclusive = ui_override(); - for (size_t i = 0; i < ui_count; i++) { - UI *ui = uis[i]; - width = MIN(ui->width, width); - height = MIN(ui->height, height); - foo = BAR(ui->bazaar, bazaar); - for (UIExtension j = 0; (int)j < kUIExtCount; j++) { - ext_widgets[j] &= (ui->ui_ext[j] || inclusive); - } - } -}]] - - it('allows to iterate over nodes children', function() - if not check_parser() then return end - - insert(test_text); - - local res = exec_lua([[ - parser = vim.treesitter.get_parser(0, "c") - - func_node = parser:parse()[1]:root():child(0) - - res = {} - for node, field in func_node:iter_children() do - table.insert(res, {node:type(), field}) - end - return res - ]]) - - eq({ - {"primitive_type", "type"}, - {"function_declarator", "declarator"}, - {"compound_statement", "body"} - }, res) - end) - - it('allows to get a child by field', function() - if not check_parser() then return end - - insert(test_text); - - local res = exec_lua([[ - parser = vim.treesitter.get_parser(0, "c") - - func_node = parser:parse()[1]:root():child(0) - - local res = {} - for _, node in ipairs(func_node:field("type")) do - table.insert(res, {node:type(), node:range()}) - end - return res - ]]) - - eq({{ "primitive_type", 0, 0, 0, 4 }}, res) - - local res_fail = exec_lua([[ - parser = vim.treesitter.get_parser(0, "c") - - return #func_node:field("foo") == 0 - ]]) - - assert(res_fail) - end) - - local query = [[ - ((call_expression function: (identifier) @minfunc (argument_list (identifier) @min_id)) (eq? @minfunc "MIN")) - "for" @keyword - (primitive_type) @type - (field_expression argument: (identifier) @fieldarg) - ]] - - it("supports runtime queries", function() - if not check_parser() then return end - - local ret = exec_lua [[ - return require"vim.treesitter.query".get_query("c", "highlights").captures[1] - ]] - - eq('variable', ret) - end) - - it('support query and iter by capture', function() - if not check_parser() then return end - - insert(test_text) - - local res = exec_lua([[ - cquery = vim.treesitter.parse_query("c", ...) - parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse()[1] - res = {} - for cid, node in cquery:iter_captures(tree:root(), 0, 7, 14) do - -- can't transmit node over RPC. just check the name and range - table.insert(res, {cquery.captures[cid], node:type(), node:range()}) - end - return res - ]], query) - - eq({ - { "type", "primitive_type", 8, 2, 8, 6 }, - { "keyword", "for", 9, 2, 9, 5 }, - { "type", "primitive_type", 9, 7, 9, 13 }, - { "minfunc", "identifier", 11, 12, 11, 15 }, - { "fieldarg", "identifier", 11, 16, 11, 18 }, - { "min_id", "identifier", 11, 27, 11, 32 }, - { "minfunc", "identifier", 12, 13, 12, 16 }, - { "fieldarg", "identifier", 12, 17, 12, 19 }, - { "min_id", "identifier", 12, 29, 12, 35 }, - { "fieldarg", "identifier", 13, 14, 13, 16 } - }, res) - end) - - it('support query and iter by match', function() - if not check_parser() then return end - - insert(test_text) - - local res = exec_lua([[ - cquery = vim.treesitter.parse_query("c", ...) - parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse()[1] - res = {} - for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14) do - -- can't transmit node over RPC. just check the name and range - local mrepr = {} - for cid,node in pairs(match) do - table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) - end - table.insert(res, {pattern, mrepr}) - end - return res - ]], query) - - eq({ - { 3, { { "type", "primitive_type", 8, 2, 8, 6 } } }, - { 2, { { "keyword", "for", 9, 2, 9, 5 } } }, - { 3, { { "type", "primitive_type", 9, 7, 9, 13 } } }, - { 4, { { "fieldarg", "identifier", 11, 16, 11, 18 } } }, - { 1, { { "minfunc", "identifier", 11, 12, 11, 15 }, { "min_id", "identifier", 11, 27, 11, 32 } } }, - { 4, { { "fieldarg", "identifier", 12, 17, 12, 19 } } }, - { 1, { { "minfunc", "identifier", 12, 13, 12, 16 }, { "min_id", "identifier", 12, 29, 12, 35 } } }, - { 4, { { "fieldarg", "identifier", 13, 14, 13, 16 } } } - }, res) - end) - - it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() - if not check_parser() then return end - - insert('char* astring = "Hello World!";') - - local res = exec_lua([[ - cquery = vim.treesitter.parse_query("c", '((_) @quote (vim-match? @quote "^\\"$")) ((_) @quote (lua-match? @quote "^\\"$"))') - parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse()[1] - res = {} - for pattern, match in cquery:iter_matches(tree:root(), 0, 0, 1) do - -- can't transmit node over RPC. just check the name and range - local mrepr = {} - for cid,node in pairs(match) do - table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) - end - table.insert(res, {pattern, mrepr}) - end - return res - ]]) - - eq({ - { 1, { { "quote", '"', 0, 16, 0, 17 } } }, - { 2, { { "quote", '"', 0, 16, 0, 17 } } }, - { 1, { { "quote", '"', 0, 29, 0, 30 } } }, - { 2, { { "quote", '"', 0, 29, 0, 30 } } }, - }, res) - end) - - it('allows to add predicates', function() - insert([[ - int main(void) { - return 0; - } - ]]) - - local custom_query = "((identifier) @main (#is-main? @main))" - - local res = exec_lua([[ - local query = require"vim.treesitter.query" - - local function is_main(match, pattern, bufnr, predicate) - local node = match[ predicate[2] ] - - return query.get_node_text(node, bufnr) - end - - local parser = vim.treesitter.get_parser(0, "c") - - query.add_predicate("is-main?", is_main) - - local query = query.parse_query("c", ...) - - local nodes = {} - for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do - table.insert(nodes, {node:range()}) - end - - return nodes - ]], custom_query) - - eq({{0, 4, 0, 8}}, res) - - local res_list = exec_lua[[ - local query = require'vim.treesitter.query' - - local list = query.list_predicates() - - table.sort(list) - - return list - ]] - - eq({ 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list) - end) - - local hl_text = [[ -/// Schedule Lua callback on main loop's event queue -static int nlua_schedule(lua_State *const lstate) -{ - if (lua_type(lstate, 1) != LUA_TFUNCTION - || lstate != lstate) { - lua_pushliteral(lstate, "vim.schedule: expected function"); - return lua_error(lstate); - } - - LuaRef cb = nlua_ref(lstate, 1); - - multiqueue_put(main_loop.events, nlua_schedule_event, - 1, (void *)(ptrdiff_t)cb); - return 0; -}]] - -local hl_query = [[ -(ERROR) @ErrorMsg - -"if" @keyword -"else" @keyword -"for" @keyword -"return" @keyword - -"const" @type -"static" @type -"struct" @type -"enum" @type -"extern" @type - -(string_literal) @string - -(number_literal) @number -(char_literal) @string - -(type_identifier) @type -((type_identifier) @Special (#eq? @Special "LuaRef")) - -(primitive_type) @type -(sized_type_specifier) @type - -; Use lua regexes -((identifier) @Identifier (#contains? @Identifier "lua_")) -((identifier) @Constant (#lua-match? @Constant "^[A-Z_]+$")) -((identifier) @Normal (#vim-match? @Constant "^lstate$")) - -((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (#eq? @WarningMsg.left @WarningMsg.right)) - -(comment) @comment -]] - - describe('when highlighting', function() - local screen - - before_each(function() - screen = Screen.new(65, 18) - screen:attach() - screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {foreground = Screen.colors.Blue1}, - [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, - [4] = {bold = true, foreground = Screen.colors.Brown}, - [5] = {foreground = Screen.colors.Magenta}, - [6] = {foreground = Screen.colors.Red}, - [7] = {bold = true, foreground = Screen.colors.SlateBlue}, - [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - [9] = {foreground = Screen.colors.Magenta, background = Screen.colors.Red}, - [10] = {foreground = Screen.colors.Red, background = Screen.colors.Red}, - [11] = {foreground = Screen.colors.Cyan4}, - }) - end) - - it('supports highlighting', function() - if not check_parser() then return end - - insert(hl_text) - screen:expect{grid=[[ - /// Schedule Lua callback on main loop's event queue | - static int nlua_schedule(lua_State *const lstate) | - { | - if (lua_type(lstate, 1) != LUA_TFUNCTION | - || lstate != lstate) { | - lua_pushliteral(lstate, "vim.schedule: expected function"); | - return lua_error(lstate); | - } | - | - LuaRef cb = nlua_ref(lstate, 1); | - | - multiqueue_put(main_loop.events, nlua_schedule_event, | - 1, (void *)(ptrdiff_t)cb); | - return 0; | - ^} | - {1:~ }| - {1:~ }| - | - ]]} - - exec_lua([[ - local parser = vim.treesitter.get_parser(0, "c") - local highlighter = vim.treesitter.highlighter - local query = ... - test_hl = highlighter.new(parser, {queries = {c = query}}) - ]], hl_query) - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | - || {6:lstate} != {6:lstate}) { | - {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | - {4:return} {11:lua_error}(lstate); | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - ^} | - {1:~ }| - {1:~ }| - | - ]]} - - feed("5Goc<esc>dd") - - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | - || {6:lstate} != {6:lstate}) { | - {11:^lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | - {4:return} {11:lua_error}(lstate); | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - } | - {1:~ }| - {1:~ }| - | - ]]} - - feed('7Go*/<esc>') - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | - || {6:lstate} != {6:lstate}) { | - {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | - {4:return} {11:lua_error}(lstate); | - {8:*^/} | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - } | - {1:~ }| - | - ]]} - - feed('3Go/*<esc>') - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {2:/^*} | - {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | - {2: || lstate != lstate) {} | - {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | - {2: return lua_error(lstate);} | - {2:*/} | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - {8:}} | - | - ]]} - - feed("gg$") - feed("~") - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queu^E} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {2:/*} | - {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | - {2: || lstate != lstate) {} | - {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | - {2: return lua_error(lstate);} | - {2:*/} | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - {8:}} | - | - ]]} - - - feed("re") - screen:expect{grid=[[ - {2:/// Schedule Lua callback on main loop's event queu^e} | - {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | - { | - {2:/*} | - {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | - {2: || lstate != lstate) {} | - {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | - {2: return lua_error(lstate);} | - {2:*/} | - } | - | - {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | - | - multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | - {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | - {4:return} {5:0}; | - {8:}} | - | - ]]} - end) - - it("supports highlighting with custom parser", function() - if not check_parser() then return end - - screen:set_default_attr_ids({ {bold = true, foreground = Screen.colors.SeaGreen4} }) - - insert(test_text) - - screen:expect{ grid= [[ - int width = INT_MAX, height = INT_MAX; | - bool ext_widgets[kUIExtCount]; | - for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | - ext_widgets[i] = true; | - } | - | - bool inclusive = ui_override(); | - for (size_t i = 0; i < ui_count; i++) { | - UI *ui = uis[i]; | - width = MIN(ui->width, width); | - height = MIN(ui->height, height); | - foo = BAR(ui->bazaar, bazaar); | - for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | - ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | - } | - } | - ^} | - | - ]] } - - exec_lua([[ - parser = vim.treesitter.get_parser(0, "c") - query = vim.treesitter.parse_query("c", "(declaration) @decl") - - local nodes = {} - for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do - table.insert(nodes, node) - end - - parser:set_included_regions({nodes}) - - local hl = vim.treesitter.highlighter.new(parser, {queries = {c = "(identifier) @type"}}) - ]]) - - screen:expect{ grid = [[ - int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; | - bool {1:ext_widgets}[{1:kUIExtCount}]; | - for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { | - ext_widgets[i] = true; | - } | - | - bool {1:inclusive} = {1:ui_override}(); | - for (size_t {1:i} = 0; i < ui_count; i++) { | - UI *{1:ui} = {1:uis}[{1:i}]; | - width = MIN(ui->width, width); | - height = MIN(ui->height, height); | - foo = BAR(ui->bazaar, bazaar); | - for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { | - ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | - } | - } | - ^} | - | - ]] } - end) - - it("supports highlighting injected languages", function() - if not check_parser() then return end - - insert([[ - int x = INT_MAX; - #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) - #define foo void main() { \ - return 42; \ - } - ]]) - - screen:expect{grid=[[ - int x = INT_MAX; | - #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))| - #define foo void main() { \ | - return 42; \ | - } | - ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - | - ]]} - - exec_lua([[ - local parser = vim.treesitter.get_parser(0, "c", { - queries = {c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"} - }) - local highlighter = vim.treesitter.highlighter - local query = ... - test_hl = highlighter.new(parser, {queries = {c = query}}) - ]], hl_query) - - screen:expect{grid=[[ - {3:int} x = {5:INT_MAX}; | - #define {5:READ_STRING}(x, y) ({3:char_u} *)read_string((x), ({3:size_t})(y))| - #define foo {3:void} main() { \ | - {4:return} {5:42}; \ | - } | - ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - | - ]]} - end) - end) - - it('inspects language', function() - if not check_parser() then return end - - local keys, fields, symbols = unpack(exec_lua([[ - local lang = vim.treesitter.inspect_language('c') - local keys, symbols = {}, {} - for k,_ in pairs(lang) do - keys[k] = true - end - - -- symbols array can have "holes" and is thus not a valid msgpack array - -- but we don't care about the numbers here (checked in the parser test) - for _, v in pairs(lang.symbols) do - table.insert(symbols, v) - end - return {keys, lang.fields, symbols} - ]])) - - eq({fields=true, symbols=true}, keys) - - local fset = {} - for _,f in pairs(fields) do - eq("string", type(f)) - fset[f] = true - end - eq(true, fset["directive"]) - eq(true, fset["initializer"]) - - local has_named, has_anonymous - for _,s in pairs(symbols) do - eq("string", type(s[1])) - eq("boolean", type(s[2])) - if s[1] == "for_statement" and s[2] == true then - has_named = true - elseif s[1] == "|=" and s[2] == false then - has_anonymous = true - end - end - eq({true,true}, {has_named,has_anonymous}) - end) - it('allows to set simple ranges', function() - if not check_parser() then return end - - insert(test_text) - - local res = exec_lua [[ - parser = vim.treesitter.get_parser(0, "c") - return { parser:parse()[1]:root():range() } - ]] - - eq({0, 0, 19, 0}, res) - - -- The following sets the included ranges for the current parser - -- As stated here, this only includes the function (thus the whole buffer, without the last line) - local res2 = exec_lua [[ - local root = parser:parse()[1]:root() - parser:set_included_regions({{root:child(0)}}) - parser:invalidate() - return { parser:parse()[1]:root():range() } - ]] - - eq({0, 0, 18, 1}, res2) - - local range = exec_lua [[ - local res = {} - for _, region in ipairs(parser:included_regions()) do - for _, node in ipairs(region) do - table.insert(res, {node:range()}) - end - end - return res - ]] - - eq(range, { { 0, 0, 18, 1 } }) - - local range_tbl = exec_lua [[ - parser:set_included_regions { { { 0, 0, 17, 1 } } } - parser:parse() - return parser:included_regions() - ]] - - eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) - end) - it("allows to set complex ranges", function() - if not check_parser() then return end - - insert(test_text) - - local res = exec_lua [[ - parser = vim.treesitter.get_parser(0, "c") - query = vim.treesitter.parse_query("c", "(declaration) @decl") - - local nodes = {} - for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do - table.insert(nodes, node) - end - - parser:set_included_regions({nodes}) - - local root = parser:parse()[1]:root() - - local res = {} - for i=0,(root:named_child_count() - 1) do - table.insert(res, { root:named_child(i):range() }) - end - return res - ]] - - eq({ - { 2, 2, 2, 40 }, - { 3, 2, 3, 32 }, - { 4, 7, 4, 25 }, - { 8, 2, 8, 33 }, - { 9, 7, 9, 20 }, - { 10, 4, 10, 20 }, - { 14, 9, 14, 27 } }, res) - end) - - it("allows to create string parsers", function() - local ret = exec_lua [[ - local parser = vim.treesitter.get_string_parser("int foo = 42;", "c") - return { parser:parse()[1]:root():range() } - ]] - - eq({ 0, 0, 0, 13 }, ret) - end) - - it("allows to run queries with string parsers", function() - local txt = [[ - int foo = 42; - int bar = 13; - ]] - - local ret = exec_lua([[ - local str = ... - local parser = vim.treesitter.get_string_parser(str, "c") - - local nodes = {} - local query = vim.treesitter.parse_query("c", '((identifier) @id (eq? @id "foo"))') - - for _, node in query:iter_captures(parser:parse()[1]:root(), str, 0, 2) do - table.insert(nodes, { node:range() }) - end - - return nodes]], txt) - - eq({ {0, 10, 0, 13} }, ret) - end) - - describe("when creating a language tree", function() - local function get_ranges() - return exec_lua([[ - local result = {} - parser:for_each_tree(function(tree) table.insert(result, {tree:root():range()}) end) - return result - ]]) - end - - before_each(function() - insert([[ - int x = INT_MAX; - #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) - #define READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) - #define VALUE 0 - #define VALUE1 1 - #define VALUE2 2 - ]]) - end) - - describe("when parsing regions independently", function() - it("should inject a language", function() - exec_lua([[ - parser = vim.treesitter.get_parser(0, "c", { - queries = { - c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}}) - ]]) - - eq("table", exec_lua("return type(parser:children().c)")) - eq(5, exec_lua("return #parser:children().c:trees()")) - eq({ - {0, 2, 7, 0}, -- root tree - {3, 16, 3, 17}, -- VALUE 0 - {4, 17, 4, 18}, -- VALUE1 1 - {5, 17, 5, 18}, -- VALUE2 2 - {1, 28, 1, 67}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) - {2, 31, 2, 70} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) - }, get_ranges()) - end) - end) - - describe("when parsing regions combined", function() - it("should inject a language", function() - exec_lua([[ - parser = vim.treesitter.get_parser(0, "c", { - queries = { - c = "(preproc_def (preproc_arg) @c @combined) (preproc_function_def value: (preproc_arg) @c @combined)"}}) - ]]) - - eq("table", exec_lua("return type(parser:children().c)")) - eq(2, exec_lua("return #parser:children().c:trees()")) - eq({ - {0, 2, 7, 0}, -- root tree - {3, 16, 5, 18}, -- VALUE 0 - -- VALUE1 1 - -- VALUE2 2 - {1, 28, 2, 70} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) - -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) - }, get_ranges()) - end) - end) - end) - -end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index e9e1f7ec12..e253db5297 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1453,3 +1453,18 @@ describe('lua stdlib', function() end) end) end) + +describe('lua: require("mod") from packages', function() + before_each(function() + command('set rtp+=test/functional/fixtures') + end) + + it('propagates syntax error', function() + local syntax_error_msg = exec_lua [[ + local _, err = pcall(require, "syntax_error") + return err + ]] + + matches("unexpected symbol", syntax_error_msg) + end) +end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index f01d90bbeb..ec06cb0639 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -262,6 +262,48 @@ describe('LSP', function() } end) + it('client should return settings via workspace/configuration handler', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "workspace/configuration", { items = { + { section = "testSetting1" }; + { section = "testSetting2" }; + }}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "check_workspace_configuration"; + on_init = function(_client) + client = _client + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_callback = function(err, method, params, client_id) + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'start' then + exec_lua([=[ + local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID) + client.config.settings = { + testSetting1 = true; + testSetting2 = false; + }]=]) + end + if method == 'workspace/configuration' then + local result = exec_lua([=[ + local method, params = ... + return require'vim.lsp.handlers'['workspace/configuration'](err, method, params, TEST_RPC_CLIENT_ID)]=], method, params) + client.notify('workspace/configuration', result) + end + if method == 'shutdown' then + client.stop() + end + end; + } + end) + it('should verify capabilities sent', function() local expected_callbacks = { {NIL, "shutdown", {}, 1}; 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/provider/define_spec.lua b/test/functional/provider/define_spec.lua index 1d50ce0a56..12efbec326 100644 --- a/test/functional/provider/define_spec.lua +++ b/test/functional/provider/define_spec.lua @@ -136,7 +136,7 @@ local function command_specs_for(fn, sync, first_arg_factory, init) end) it('with nargs/count', function() - call(fn, args..', {"nargs": "1", "range": "5"}') + call(fn, args..', {"nargs": "1", "count": "5"}') local function on_setup() command('5RpcCommand arg') end @@ -152,7 +152,7 @@ local function command_specs_for(fn, sync, first_arg_factory, init) end) it('with nargs/count/bang', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": ""}') + call(fn, args..', {"nargs": "1", "count": "5", "bang": ""}') local function on_setup() command('5RpcCommand! arg') end @@ -169,7 +169,7 @@ local function command_specs_for(fn, sync, first_arg_factory, init) end) it('with nargs/count/bang/register', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. + call(fn, args..', {"nargs": "1", "count": "5", "bang": "",'.. ' "register": ""}') local function on_setup() command('5RpcCommand! b arg') @@ -188,7 +188,7 @@ local function command_specs_for(fn, sync, first_arg_factory, init) end) it('with nargs/count/bang/register/eval', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. + call(fn, args..', {"nargs": "1", "count": "5", "bang": "",'.. ' "register": "", "eval": "@<reg>"}') local function on_setup() command('let @b = "regb"') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 3ef41fde1d..8a5dd7ef18 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -1477,7 +1477,7 @@ describe("TUI", function() retry(nil, 3000, function() -- Wait for log file to be flushed. local log = read_file('Xtest_tui_verbose_log') or '' - eq('--- Terminal info --- {{{\n', string.match(log, '--- Terminal.-\n')) + eq('--- Terminal info --- {{{\n', string.match(log, '%-%-%- Terminal.-\n')) ok(#log > 50) end) end) diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua new file mode 100644 index 0000000000..cb73bfbbe1 --- /dev/null +++ b/test/functional/treesitter/highlight_spec.lua @@ -0,0 +1,475 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') + +local clear = helpers.clear +local insert = helpers.insert +local exec_lua = helpers.exec_lua +local feed = helpers.feed +local pending_c_parser = helpers.pending_c_parser + +before_each(clear) + +local hl_query = [[ + (ERROR) @ErrorMsg + + "if" @keyword + "else" @keyword + "for" @keyword + "return" @keyword + + "const" @type + "static" @type + "struct" @type + "enum" @type + "extern" @type + + (string_literal) @string + + (number_literal) @number + (char_literal) @string + + (type_identifier) @type + ((type_identifier) @Special (#eq? @Special "LuaRef")) + + (primitive_type) @type + (sized_type_specifier) @type + + ; Use lua regexes + ((identifier) @Identifier (#contains? @Identifier "lua_")) + ((identifier) @Constant (#lua-match? @Constant "^[A-Z_]+$")) + ((identifier) @Normal (#vim-match? @Constant "^lstate$")) + + ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (#eq? @WarningMsg.left @WarningMsg.right)) + + (comment) @comment +]] + +local hl_text = [[ +/// Schedule Lua callback on main loop's event queue +static int nlua_schedule(lua_State *const lstate) +{ + if (lua_type(lstate, 1) != LUA_TFUNCTION + || lstate != lstate) { + lua_pushliteral(lstate, "vim.schedule: expected function"); + return lua_error(lstate); + } + + LuaRef cb = nlua_ref(lstate, 1); + + multiqueue_put(main_loop.events, nlua_schedule_event, + 1, (void *)(ptrdiff_t)cb); + return 0; +}]] + +local test_text = [[ +void ui_refresh(void) +{ + int width = INT_MAX, height = INT_MAX; + bool ext_widgets[kUIExtCount]; + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { + ext_widgets[i] = true; + } + + bool inclusive = ui_override(); + for (size_t i = 0; i < ui_count; i++) { + UI *ui = uis[i]; + width = MIN(ui->width, width); + height = MIN(ui->height, height); + foo = BAR(ui->bazaar, bazaar); + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); + } + } +}]] + +describe('treesitter highlighting', function() + local screen + + before_each(function() + screen = Screen.new(65, 18) + screen:attach() + screen:set_default_attr_ids { + [1] = {bold = true, foreground = Screen.colors.Blue1}; + [2] = {foreground = Screen.colors.Blue1}; + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}; + [4] = {bold = true, foreground = Screen.colors.Brown}; + [5] = {foreground = Screen.colors.Magenta}; + [6] = {foreground = Screen.colors.Red}; + [7] = {bold = true, foreground = Screen.colors.SlateBlue}; + [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}; + [9] = {foreground = Screen.colors.Magenta, background = Screen.colors.Red}; + [10] = {foreground = Screen.colors.Red, background = Screen.colors.Red}; + [11] = {foreground = Screen.colors.Cyan4}; + } + + exec_lua([[ hl_query = ... ]], hl_query) + end) + + it('is updated with edits', function() + if pending_c_parser(pending) then return end + + insert(hl_text) + screen:expect{grid=[[ + /// Schedule Lua callback on main loop's event queue | + static int nlua_schedule(lua_State *const lstate) | + { | + if (lua_type(lstate, 1) != LUA_TFUNCTION | + || lstate != lstate) { | + lua_pushliteral(lstate, "vim.schedule: expected function"); | + return lua_error(lstate); | + } | + | + LuaRef cb = nlua_ref(lstate, 1); | + | + multiqueue_put(main_loop.events, nlua_schedule_event, | + 1, (void *)(ptrdiff_t)cb); | + return 0; | + ^} | + {1:~ }| + {1:~ }| + | + ]]} + + exec_lua [[ + local parser = vim.treesitter.get_parser(0, "c") + local highlighter = vim.treesitter.highlighter + test_hl = highlighter.new(parser, {queries = {c = hl_query}}) + ]] + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + ^} | + {1:~ }| + {1:~ }| + | + ]]} + + feed("5Goc<esc>dd") + + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:^lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + } | + {1:~ }| + {1:~ }| + | + ]]} + + feed('7Go*/<esc>') + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + {8:*^/} | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + } | + {1:~ }| + | + ]]} + + feed('3Go/*<esc>') + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {2:/^*} | + {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | + {2: || lstate != lstate) {} | + {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | + {2: return lua_error(lstate);} | + {2:*/} | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + {8:}} | + | + ]]} + + feed("gg$") + feed("~") + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queu^E} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {2:/*} | + {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | + {2: || lstate != lstate) {} | + {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | + {2: return lua_error(lstate);} | + {2:*/} | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + {8:}} | + | + ]]} + + + feed("re") + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queu^e} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {2:/*} | + {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | + {2: || lstate != lstate) {} | + {2: lua_pushliteral(lstate, "vim.schedule: expected function");} | + {2: return lua_error(lstate);} | + {2:*/} | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + {8:}} | + | + ]]} + end) + + it('is updated with :sort', function() + if pending_c_parser(pending) then return end + + insert(test_text) + exec_lua [[ + local parser = vim.treesitter.get_parser(0, "c") + test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query}}) + ]] + screen:expect{grid=[[ + {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; | + {3:bool} ext_widgets[kUIExtCount]; | + {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + {3:bool} inclusive = ui_override(); | + {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { | + {3:UI} *ui = uis[i]; | + width = {5:MIN}(ui->width, width); | + height = {5:MIN}(ui->height, height); | + foo = {5:BAR}(ui->bazaar, bazaar); | + {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]]} + + feed ":sort<cr>" + screen:expect{grid=[[ + ^ | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + {3:UI} *ui = uis[i]; | + ext_widgets[i] = true; | + foo = {5:BAR}(ui->bazaar, bazaar); | + {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { | + height = {5:MIN}(ui->height, height); | + width = {5:MIN}(ui->width, width); | + } | + {3:bool} ext_widgets[kUIExtCount]; | + {3:bool} inclusive = ui_override(); | + {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { | + {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { | + {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; | + } | + } | + {3:void} ui_refresh({3:void}) | + :sort | + ]]} + + feed "u" + + screen:expect{grid=[[ + {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; | + {3:bool} ext_widgets[kUIExtCount]; | + {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + {3:bool} inclusive = ui_override(); | + {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { | + {3:UI} *ui = uis[i]; | + width = {5:MIN}(ui->width, width); | + height = {5:MIN}(ui->height, height); | + foo = {5:BAR}(ui->bazaar, bazaar); | + {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + 19 changes; before #2 {MATCH:.*}| + ]]} + end) + + it("supports with custom parser", function() + if pending_c_parser(pending) then return end + + screen:set_default_attr_ids { + [1] = {bold = true, foreground = Screen.colors.SeaGreen4}; + } + + insert(test_text) + + screen:expect{ grid= [[ + int width = INT_MAX, height = INT_MAX; | + bool ext_widgets[kUIExtCount]; | + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool inclusive = ui_override(); | + for (size_t i = 0; i < ui_count; i++) { | + UI *ui = uis[i]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + + exec_lua [[ + parser = vim.treesitter.get_parser(0, "c") + query = vim.treesitter.parse_query("c", "(declaration) @decl") + + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do + table.insert(nodes, node) + end + + parser:set_included_regions({nodes}) + + local hl = vim.treesitter.highlighter.new(parser, {queries = {c = "(identifier) @type"}}) + ]] + + screen:expect{ grid = [[ + int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; | + bool {1:ext_widgets}[{1:kUIExtCount}]; | + for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool {1:inclusive} = {1:ui_override}(); | + for (size_t {1:i} = 0; i < ui_count; i++) { | + UI *{1:ui} = {1:uis}[{1:i}]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + end) + + it("supports injected languages", function() + if pending_c_parser(pending) then return end + + insert([[ + int x = INT_MAX; + #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + #define foo void main() { \ + return 42; \ + } + ]]) + + screen:expect{grid=[[ + int x = INT_MAX; | + #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))| + #define foo void main() { \ | + return 42; \ | + } | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + exec_lua [[ + local parser = vim.treesitter.get_parser(0, "c", { + queries = {c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"} + }) + local highlighter = vim.treesitter.highlighter + test_hl = highlighter.new(parser, {queries = {c = hl_query}}) + ]] + + screen:expect{grid=[[ + {3:int} x = {5:INT_MAX}; | + #define {5:READ_STRING}(x, y) ({3:char_u} *)read_string((x), ({3:size_t})(y))| + #define foo {3:void} main() { \ | + {4:return} {5:42}; \ | + } | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) +end) diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua new file mode 100644 index 0000000000..a5801271cb --- /dev/null +++ b/test/functional/treesitter/language_spec.lua @@ -0,0 +1,71 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local pcall_err = helpers.pcall_err +local matches = helpers.matches +local pending_c_parser = helpers.pending_c_parser + +before_each(clear) + +describe('treesitter API', function() + -- error tests not requiring a parser library + it('handles missing language', function() + eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", + pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")) + + -- actual message depends on platform + matches("Error executing lua: Failed to load parser: uv_dlopen: .+", + pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')")) + + -- Should not throw an error when silent + eq(false, exec_lua("return vim.treesitter.require_language('borklang', nil, true)")) + eq(false, exec_lua("return vim.treesitter.require_language('borklang', 'borkbork.so', true)")) + + eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", + pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')")) + end) + + it('inspects language', function() + if pending_c_parser(pending) then return end + + local keys, fields, symbols = unpack(exec_lua([[ + local lang = vim.treesitter.inspect_language('c') + local keys, symbols = {}, {} + for k,_ in pairs(lang) do + keys[k] = true + end + + -- symbols array can have "holes" and is thus not a valid msgpack array + -- but we don't care about the numbers here (checked in the parser test) + for _, v in pairs(lang.symbols) do + table.insert(symbols, v) + end + return {keys, lang.fields, symbols} + ]])) + + eq({fields=true, symbols=true}, keys) + + local fset = {} + for _,f in pairs(fields) do + eq("string", type(f)) + fset[f] = true + end + eq(true, fset["directive"]) + eq(true, fset["initializer"]) + + local has_named, has_anonymous + for _,s in pairs(symbols) do + eq("string", type(s[1])) + eq("boolean", type(s[2])) + if s[1] == "for_statement" and s[2] == true then + has_named = true + elseif s[1] == "|=" and s[2] == false then + has_anonymous = true + end + end + eq({true,true}, {has_named,has_anonymous}) + end) +end) + diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua new file mode 100644 index 0000000000..520574c08a --- /dev/null +++ b/test/functional/treesitter/parser_spec.lua @@ -0,0 +1,576 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local insert = helpers.insert +local exec_lua = helpers.exec_lua +local feed = helpers.feed +local pending_c_parser = helpers.pending_c_parser + +before_each(clear) + +describe('treesitter parser API', function() + + it('parses buffer', function() + if helpers.pending_win32(pending) or pending_c_parser(pending) then return end + + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + root = tree:root() + lang = vim.treesitter.inspect_language('c') + ]]) + + eq("<tree>", exec_lua("return tostring(tree)")) + eq("<node translation_unit>", exec_lua("return tostring(root)")) + eq({0,0,3,0}, exec_lua("return {root:range()}")) + + eq(1, exec_lua("return root:child_count()")) + exec_lua("child = root:child(0)") + eq("<node function_definition>", exec_lua("return tostring(child)")) + eq({0,0,2,1}, exec_lua("return {child:range()}")) + + eq("function_definition", exec_lua("return child:type()")) + eq(true, exec_lua("return child:named()")) + eq("number", type(exec_lua("return child:symbol()"))) + eq({'function_definition', true}, exec_lua("return lang.symbols[child:symbol()]")) + + exec_lua("anon = root:descendant_for_range(0,8,0,9)") + eq("(", exec_lua("return anon:type()")) + eq(false, exec_lua("return anon:named()")) + eq("number", type(exec_lua("return anon:symbol()"))) + eq({'(', false}, exec_lua("return lang.symbols[anon:symbol()]")) + + exec_lua("descendant = root:descendant_for_range(1,2,1,12)") + eq("<node declaration>", exec_lua("return tostring(descendant)")) + eq({1,2,1,12}, exec_lua("return {descendant:range()}")) + eq("(declaration type: (primitive_type) declarator: (init_declarator declarator: (identifier) value: (number_literal)))", exec_lua("return descendant:sexpr()")) + + feed("2G7|ay") + exec_lua([[ + tree2 = parser:parse()[1] + root2 = tree2:root() + descendant2 = root2:descendant_for_range(1,2,1,13) + ]]) + eq(false, exec_lua("return tree2 == tree1")) + eq(false, exec_lua("return root2 == root")) + eq("<node declaration>", exec_lua("return tostring(descendant2)")) + eq({1,2,1,13}, exec_lua("return {descendant2:range()}")) + + eq(true, exec_lua("return child == child")) + -- separate lua object, but represents same node + eq(true, exec_lua("return child == root:child(0)")) + eq(false, exec_lua("return child == descendant2")) + eq(false, exec_lua("return child == nil")) + eq(false, exec_lua("return child == tree")) + + eq("string", exec_lua("return type(child:id())")) + eq(true, exec_lua("return child:id() == child:id()")) + -- separate lua object, but represents same node + eq(true, exec_lua("return child:id() == root:child(0):id()")) + eq(false, exec_lua("return child:id() == descendant2:id()")) + eq(false, exec_lua("return child:id() == nil")) + eq(false, exec_lua("return child:id() == tree")) + + -- unchanged buffer: return the same tree + eq(true, exec_lua("return parser:parse()[1] == tree2")) + end) + + local test_text = [[ +void ui_refresh(void) +{ + int width = INT_MAX, height = INT_MAX; + bool ext_widgets[kUIExtCount]; + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { + ext_widgets[i] = true; + } + + bool inclusive = ui_override(); + for (size_t i = 0; i < ui_count; i++) { + UI *ui = uis[i]; + width = MIN(ui->width, width); + height = MIN(ui->height, height); + foo = BAR(ui->bazaar, bazaar); + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); + } + } +}]] + + it('allows to iterate over nodes children', function() + if pending_c_parser(pending) then return end + + insert(test_text); + + local res = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + func_node = parser:parse()[1]:root():child(0) + + res = {} + for node, field in func_node:iter_children() do + table.insert(res, {node:type(), field}) + end + return res + ]]) + + eq({ + {"primitive_type", "type"}, + {"function_declarator", "declarator"}, + {"compound_statement", "body"} + }, res) + end) + + it('allows to get a child by field', function() + if pending_c_parser(pending) then return end + + insert(test_text); + + local res = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + func_node = parser:parse()[1]:root():child(0) + + local res = {} + for _, node in ipairs(func_node:field("type")) do + table.insert(res, {node:type(), node:range()}) + end + return res + ]]) + + eq({{ "primitive_type", 0, 0, 0, 4 }}, res) + + local res_fail = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + return #func_node:field("foo") == 0 + ]]) + + assert(res_fail) + end) + + local query = [[ + ((call_expression function: (identifier) @minfunc (argument_list (identifier) @min_id)) (eq? @minfunc "MIN")) + "for" @keyword + (primitive_type) @type + (field_expression argument: (identifier) @fieldarg) + ]] + + it("supports runtime queries", function() + if pending_c_parser(pending) then return end + + local ret = exec_lua [[ + return require"vim.treesitter.query".get_query("c", "highlights").captures[1] + ]] + + eq('variable', ret) + end) + + it('support query and iter by capture', function() + if pending_c_parser(pending) then return end + + insert(test_text) + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", ...) + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for cid, node in cquery:iter_captures(tree:root(), 0, 7, 14) do + -- can't transmit node over RPC. just check the name and range + table.insert(res, {cquery.captures[cid], node:type(), node:range()}) + end + return res + ]], query) + + eq({ + { "type", "primitive_type", 8, 2, 8, 6 }, + { "keyword", "for", 9, 2, 9, 5 }, + { "type", "primitive_type", 9, 7, 9, 13 }, + { "minfunc", "identifier", 11, 12, 11, 15 }, + { "fieldarg", "identifier", 11, 16, 11, 18 }, + { "min_id", "identifier", 11, 27, 11, 32 }, + { "minfunc", "identifier", 12, 13, 12, 16 }, + { "fieldarg", "identifier", 12, 17, 12, 19 }, + { "min_id", "identifier", 12, 29, 12, 35 }, + { "fieldarg", "identifier", 13, 14, 13, 16 } + }, res) + end) + + it('support query and iter by match', function() + if pending_c_parser(pending) then return end + + insert(test_text) + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", ...) + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid,node in pairs(match) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end + table.insert(res, {pattern, mrepr}) + end + return res + ]], query) + + eq({ + { 3, { { "type", "primitive_type", 8, 2, 8, 6 } } }, + { 2, { { "keyword", "for", 9, 2, 9, 5 } } }, + { 3, { { "type", "primitive_type", 9, 7, 9, 13 } } }, + { 4, { { "fieldarg", "identifier", 11, 16, 11, 18 } } }, + { 1, { { "minfunc", "identifier", 11, 12, 11, 15 }, { "min_id", "identifier", 11, 27, 11, 32 } } }, + { 4, { { "fieldarg", "identifier", 12, 17, 12, 19 } } }, + { 1, { { "minfunc", "identifier", 12, 13, 12, 16 }, { "min_id", "identifier", 12, 29, 12, 35 } } }, + { 4, { { "fieldarg", "identifier", 13, 14, 13, 16 } } } + }, res) + end) + + it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() + if pending_c_parser(pending) then return end + + insert('char* astring = "Hello World!";') + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", '((_) @quote (vim-match? @quote "^\\"$")) ((_) @quote (lua-match? @quote "^\\"$"))') + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for pattern, match in cquery:iter_matches(tree:root(), 0, 0, 1) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid,node in pairs(match) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end + table.insert(res, {pattern, mrepr}) + end + return res + ]]) + + eq({ + { 1, { { "quote", '"', 0, 16, 0, 17 } } }, + { 2, { { "quote", '"', 0, 16, 0, 17 } } }, + { 1, { { "quote", '"', 0, 29, 0, 30 } } }, + { 2, { { "quote", '"', 0, 29, 0, 30 } } }, + }, res) + end) + + it('allows to add predicates', function() + insert([[ + int main(void) { + return 0; + } + ]]) + + local custom_query = "((identifier) @main (#is-main? @main))" + + local res = exec_lua([[ + local query = require"vim.treesitter.query" + + local function is_main(match, pattern, bufnr, predicate) + local node = match[ predicate[2] ] + + return query.get_node_text(node, bufnr) + end + + local parser = vim.treesitter.get_parser(0, "c") + + query.add_predicate("is-main?", is_main) + + local query = query.parse_query("c", ...) + + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do + table.insert(nodes, {node:range()}) + end + + return nodes + ]], custom_query) + + eq({{0, 4, 0, 8}}, res) + + local res_list = exec_lua[[ + local query = require'vim.treesitter.query' + + local list = query.list_predicates() + + table.sort(list) + + return list + ]] + + eq({ 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list) + end) + + + it('allows to set simple ranges', function() + if pending_c_parser(pending) then return end + + insert(test_text) + + local res = exec_lua [[ + parser = vim.treesitter.get_parser(0, "c") + return { parser:parse()[1]:root():range() } + ]] + + eq({0, 0, 19, 0}, res) + + -- The following sets the included ranges for the current parser + -- As stated here, this only includes the function (thus the whole buffer, without the last line) + local res2 = exec_lua [[ + local root = parser:parse()[1]:root() + parser:set_included_regions({{root:child(0)}}) + parser:invalidate() + return { parser:parse()[1]:root():range() } + ]] + + eq({0, 0, 18, 1}, res2) + + local range = exec_lua [[ + local res = {} + for _, region in ipairs(parser:included_regions()) do + for _, node in ipairs(region) do + table.insert(res, {node:range()}) + end + end + return res + ]] + + eq(range, { { 0, 0, 18, 1 } }) + + local range_tbl = exec_lua [[ + parser:set_included_regions { { { 0, 0, 17, 1 } } } + parser:parse() + return parser:included_regions() + ]] + + eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) + end) + it("allows to set complex ranges", function() + if pending_c_parser() then return end + + insert(test_text) + + local res = exec_lua [[ + parser = vim.treesitter.get_parser(0, "c") + query = vim.treesitter.parse_query("c", "(declaration) @decl") + + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do + table.insert(nodes, node) + end + + parser:set_included_regions({nodes}) + + local root = parser:parse()[1]:root() + + local res = {} + for i=0,(root:named_child_count() - 1) do + table.insert(res, { root:named_child(i):range() }) + end + return res + ]] + + eq({ + { 2, 2, 2, 40 }, + { 3, 2, 3, 32 }, + { 4, 7, 4, 25 }, + { 8, 2, 8, 33 }, + { 9, 7, 9, 20 }, + { 10, 4, 10, 20 }, + { 14, 9, 14, 27 } }, res) + end) + + it("allows to create string parsers", function() + local ret = exec_lua [[ + local parser = vim.treesitter.get_string_parser("int foo = 42;", "c") + return { parser:parse()[1]:root():range() } + ]] + + eq({ 0, 0, 0, 13 }, ret) + end) + + it("allows to run queries with string parsers", function() + local txt = [[ + int foo = 42; + int bar = 13; + ]] + + local ret = exec_lua([[ + local str = ... + local parser = vim.treesitter.get_string_parser(str, "c") + + local nodes = {} + local query = vim.treesitter.parse_query("c", '((identifier) @id (eq? @id "foo"))') + + for _, node in query:iter_captures(parser:parse()[1]:root(), str, 0, 2) do + table.insert(nodes, { node:range() }) + end + + return nodes]], txt) + + eq({ {0, 10, 0, 13} }, ret) + end) + + describe("when creating a language tree", function() + local function get_ranges() + return exec_lua([[ + local result = {} + parser:for_each_tree(function(tree) table.insert(result, {tree:root():range()}) end) + return result + ]]) + end + + before_each(function() + insert([[ +int x = INT_MAX; +#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) +#define READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) +#define VALUE 123 +#define VALUE1 123 +#define VALUE2 123 + ]]) + end) + + describe("when parsing regions independently", function() + it("should inject a language", function() + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { + c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}}) + ]]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq(5, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 0, 7, 0}, -- root tree + {3, 14, 3, 17}, -- VALUE 123 + {4, 15, 4, 18}, -- VALUE1 123 + {5, 15, 5, 18}, -- VALUE2 123 + {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + + describe("when parsing regions combined", function() + it("should inject a language", function() + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { + c = "(preproc_def (preproc_arg) @c @combined) (preproc_function_def value: (preproc_arg) @c @combined)"}}) + ]]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq(2, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 0, 7, 0}, -- root tree + {3, 14, 5, 18}, -- VALUE 123 + -- VALUE1 123 + -- VALUE2 123 + {1, 26, 2, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + + describe("when using the offset directive", function() + it("should shift the range by the directive amount", function() + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { + c = "(preproc_def ((preproc_arg) @c (#offset! @c 0 2 0 -1))) (preproc_function_def value: (preproc_arg) @c)"}}) + ]]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq({ + {0, 0, 7, 0}, -- root tree + {3, 15, 3, 16}, -- VALUE 123 + {4, 16, 4, 17}, -- VALUE1 123 + {5, 16, 5, 17}, -- VALUE2 123 + {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + end) + + describe("when getting the language for a range", function() + before_each(function() + insert([[ +int x = INT_MAX; +#define VALUE 123456789 + ]]) + end) + + it("should return the correct language tree", function() + local result = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { c = "(preproc_def (preproc_arg) @c)"}}) + + local sub_tree = parser:language_for_range({1, 18, 1, 19}) + + return sub_tree == parser:children().c + ]]) + + eq(result, true) + end) + end) + + describe("when getting/setting match data", function() + describe("when setting for the whole match", function() + it("should set/get the data correctly", function() + insert([[ + int x = 3; + ]]) + + local result = exec_lua([[ + local result + + query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! "key" "value"))') + parser = vim.treesitter.get_parser(0, "c") + + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, 1) do + result = metadata.key + end + + return result + ]]) + + eq(result, "value") + end) + end) + + describe("when setting for a capture match", function() + it("should set/get the data correctly", function() + insert([[ + int x = 3; + ]]) + + local result = exec_lua([[ + local result + + query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value"))') + parser = vim.treesitter.get_parser(0, "c") + + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, 1) do + result = metadata[pattern].key + end + + return result + ]]) + + eq(result, "value") + end) + end) + end) +end) diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 7b05e90459..6ce8b33a63 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -886,6 +886,41 @@ describe("folded lines", function() | ]]) end + command("set foldcolumn=auto") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {7:+}{5:^+-- 2 lines: line 1························}| + {7: }line 3 | + {7: }line 4 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ]], unchanged=true} + else + screen:expect{grid=[[ + {7:+}{5:^+-- 2 lines: line 1························}| + {7: }line 3 | + {7: }line 4 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], unchanged=true} + end -- fdc should not change with a new fold as the maximum is 1 feed("zf3j") @@ -924,6 +959,41 @@ describe("folded lines", function() ]]) end + command("set foldcolumn=auto:1") + if multigrid then screen:expect{grid=[[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {7:+}{5:^+-- 4 lines: line 1························}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ]], unchanged=true} + else + screen:expect{grid=[[ + {7:+}{5:^+-- 4 lines: line 1························}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], unchanged=true} + end + -- relax the maximum fdc thus fdc should expand to -- accomodate the current number of folds command("set foldcolumn=auto:4") diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index a741136111..7bca741ae3 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -45,13 +45,33 @@ describe('ui/mouse/input', function() it('single left click moves cursor', function() feed('<LeftMouse><2,1>') - screen:expect([[ + screen:expect{grid=[[ testing | mo^use | support and selection | {0:~ }| | + ]], mouse_enabled=true} + feed('<LeftMouse><0,0>') + screen:expect([[ + ^testing | + mouse | + support and selection | + {0:~ }| + | ]]) + end) + + it("in external ui works with unset 'mouse'", function() + meths.set_option('mouse', '') + feed('<LeftMouse><2,1>') + screen:expect{grid=[[ + testing | + mo^use | + support and selection | + {0:~ }| + | + ]], mouse_enabled=false} feed('<LeftMouse><0,0>') screen:expect([[ ^testing | diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index 6601c2d68e..719e2ee82a 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -1562,6 +1562,77 @@ describe('ext_multigrid', function() {1:~ }| ]]} + command('tabnext') + command('$tabnew') + screen:expect{grid=[[ + ## grid 1 + {16: }{17:2}{16: [No Name] }{17:2}{16: [No Name] }{7: [No Name] }{12: }{16:X}| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + [7:-----------------------------------------------------]| + {11:[No Name] }| + [3:-----------------------------------------------------]| + ## grid 2 (hidden) + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 4 (hidden) + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 5 (hidden) + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 6 (hidden) + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 7 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + + command('tabclose') command('tabclose') screen:expect{grid=[[ ## grid 1 diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 8fa9fcc42f..fcf6926433 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -170,7 +170,7 @@ function Screen.new(width, height) ruler = {}, hl_groups = {}, _default_attr_ids = nil, - _mouse_enabled = true, + mouse_enabled = true, _attrs = {}, _hl_info = {[0]={}}, _attr_table = {[0]={{},{}}}, @@ -318,7 +318,7 @@ function Screen:expect(expected, attr_ids, ...) assert(next({...}) == nil, "invalid args to expect()") if type(expected) == "table" then assert(not (attr_ids ~= nil)) - local is_key = {grid=true, attr_ids=true, condition=true, + local is_key = {grid=true, attr_ids=true, condition=true, mouse_enabled=true, any=true, mode=true, unchanged=true, intermediate=true, reset=true, timeout=true, request_cb=true, hl_groups=true} for _, v in ipairs(ext_keys) do @@ -422,12 +422,15 @@ screen:redraw_debug() to show all intermediate screen states. ]]) if expected.mode ~= nil then extstate.mode = self.mode end + if expected.mouse_enabled ~= nil then + extstate.mouse_enabled = self.mouse_enabled + end if expected.win_viewport == nil then extstate.win_viewport = nil end -- Convert assertion errors into invalid screen state descriptions. - for _, k in ipairs(concat_tables(ext_keys, {'mode'})) do + for _, k in ipairs(concat_tables(ext_keys, {'mode', 'mouse_enabled'})) do -- Empty states are considered the default and need not be mentioned. if (not (expected[k] == nil and isempty(extstate[k]))) then local status, res = pcall(eq, expected[k], extstate[k], k) @@ -799,11 +802,11 @@ function Screen:_handle_busy_stop() end function Screen:_handle_mouse_on() - self._mouse_enabled = true + self.mouse_enabled = true end function Screen:_handle_mouse_off() - self._mouse_enabled = false + self.mouse_enabled = false end function Screen:_handle_mode_change(mode, idx) 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') diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index d8923fd65b..5af5eb0a49 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -147,8 +147,8 @@ set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/cpp-3.0.0 set(MSGPACK_SHA256 bfbb71b7c02f806393bc3cbc491b40523b89e64f83860c58e3e54af47de176e4) # https://github.com/LuaJIT/LuaJIT/tree/v2.1 -set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/f0e865dd4861520258299d0f2a56491bd9d602e1.tar.gz) -set(LUAJIT_SHA256 ad5077bd861241bf5e50ae4bf543d291c5fcffab95ccc3218401131f503e45bd) +set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/1d8b747c161db457e032a023ebbff511f5de5ec2.tar.gz) +set(LUAJIT_SHA256 20a159c38a98ecdb6368e8d655343b6036622a29a1621da9dc303f7ed9bf37f3) set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz) set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333) diff --git a/third-party/cmake/BuildLuajit.cmake b/third-party/cmake/BuildLuajit.cmake index c0b24fb2a5..ca41a7cee3 100644 --- a/third-party/cmake/BuildLuajit.cmake +++ b/third-party/cmake/BuildLuajit.cmake @@ -64,9 +64,12 @@ set(INSTALLCMD_UNIX ${MAKE_PRG} CFLAGS=-fPIC if(UNIX) if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - # Set MACOSX_DEPLOYMENT_TARGET (else luajit defaults to 10.4). #9050 - # https://github.com/LuaJIT/LuaJIT/blob/b025b01c5b9d23f6218c7d72b7aafa3f1ab1e08a/src/Makefile#L301-L303 - set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") + if(CMAKE_OSX_DEPLOYMENT_TARGET) + set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") + else() + # Use the same target as our nightly builds + set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=10.11") + endif() else() set(DEPLOYMENT_TARGET "") endif() |